From f9066a5926a892535b93ce29c21bc5c47f029d17 Mon Sep 17 00:00:00 2001 From: darcywong00 Date: Fri, 15 Sep 2017 10:23:51 +0700 Subject: [PATCH 01/19] Start adding chirality modifiers to KMEA This is follow-on to #300 for adding chirality modifiers to KMEA. Add support for L/R Ctrl, Alt, caps lock, num lock, and scroll lock for external keyboard - temporarily adding test chriality.js keyboard --- .../KMEA/app/src/main/assets/keyboard.html | 10 ++- .../src/main/assets/languages/chirality.js | 69 ++++++++++++++++++ .../kmea/KMHardwareKeyboardInterpreter.java | 73 +++++++++++++++++-- .../java/com/tavultesoft/kmea/KMKeyboard.java | 7 +- .../java/com/tavultesoft/kmea/KMManager.java | 33 +++++++-- android/history.md | 1 + web/source/kmwembedded.js | 9 ++- 7 files changed, 179 insertions(+), 23 deletions(-) create mode 100644 android/KMEA/app/src/main/assets/languages/chirality.js diff --git a/android/KMEA/app/src/main/assets/keyboard.html b/android/KMEA/app/src/main/assets/keyboard.html index b2301c4a71..c21c714be7 100644 --- a/android/KMEA/app/src/main/assets/keyboard.html +++ b/android/KMEA/app/src/main/assets/keyboard.html @@ -56,6 +56,10 @@ return oskWidth; } + function isChiral() { + return tavultesoft.keymanweb.isChiral(); + } + function setKeymanLanguage(keyboardName, internalName, languageName, langId, version, font, oskFont) { //oskFont.files = ['NotoSansSyriacWestern-Regular.svg#NotoSansSyriacWesternRegular']; //window.console.log('oskFonts = '+JSON.stringify(oskFont)); @@ -172,11 +176,11 @@ kmw['executePopupKey'](keyID, keyText); } - function executeHardwareKeystroke(code, shift) { + function executeHardwareKeystroke(code, shift, lstates) { var kmw=window['tavultesoft']['keymanweb']; - window.console.log('executeHardwareKeystroke:('+code+', ' + shift + ');'); + window.console.log('executeHardwareKeystroke:('+code+', ' + shift + ', ' + lstates + ');'); try { - var r = kmw['executeHardwareKeystroke'](code, shift); + var r = kmw['executeHardwareKeystroke'](code, shift, lstates); window.console.log('executeHardwareKeystroke completed with '+r); } catch(e) { window.console.log('oops: '+e); diff --git a/android/KMEA/app/src/main/assets/languages/chirality.js b/android/KMEA/app/src/main/assets/languages/chirality.js new file mode 100644 index 0000000000..5f2f0075f1 --- /dev/null +++ b/android/KMEA/app/src/main/assets/languages/chirality.js @@ -0,0 +1,69 @@ +KeymanWeb.KR(new Keyboard_chirality()); + +function Keyboard_chirality() { + this.KI = "Keyboard_chirality"; + this.KN = "Chirality Keyboard"; + this.KV = { + KMBM: 0x001F, + F: ' 1em "Arial"', + K102: 0, + KLS: { 'default': new Array("`", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "", "", "", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\\", "", "", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "", "", "", "", "", "", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "", "", "", "", "", ""), + 'shift': new Array("~", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "", "", "", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{", "}", "|", "", "", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "", "", "", "", "", "", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "", "", "", "", "", ""), + 'leftctrl': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ʍ", "ə", "ɹ", "ð", "ʏ", "ʉ", "ɨ", "ɵ", "ʘ", "", "", "", "", "", "", "ɑ", "ʃ", "", "ɸ", "ɣ", "ɥ", "ɟ", "", "ɬ", "ː", "", "", "", "", "", "", "", "ʒ", "χ", "ç", "ʋ", "β", "ɲ", "", "", "", "", "", "", "", "", "", ""), + 'leftctrl-shift': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ʡ", "", "ɘ", "ʀ", "θ", "", "ɯ", "ɪ", "", "", "", "", "", "", "", "", "ɒ", "ᶘ", "", "", "ɢ", "ʜ", "", "", "ʟ", "ː", "", "", "", "", "", "", "", "ᶚ", "", "", "", "ʙ", "ɴ", "", "", "", "ʔ", "", "", "", "", "", ""), + 'leftalt': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ɛ", "ɽ", "ʈ", "ɥ", "ʊ", "", "ɔ", "", "", "", "", "", "", "", "æ", "", "ɖ", "", "", "ɦ", "ʝ", "", "ɭ", "", "", "", "", "", "", "", "", "ʐ", "", "ɕ", "", "", "ɳ", "", "", "", "", "", "", "", "", "", ""), + 'rightalt': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ʠ", "ɰ", "ɜ", "ɾ", "ƭ", "", "ʌ", "", "ø", "ƥ", "", "", "", "", "", "", "ɐ", "σ", "ɗ", "", "ɠ", "ħ", "ʄ", "ƙ", "ɮ", "", "", "", "", "", "", "", "", "ʑ", "", "ƈ", "", "ɓ", "ŋ", "ɱ", "", "", "", "", "", "", "", "", ""), + 'leftalt-shift': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ʢ", "", "œ", "ɻ", "", "", "", "ᵻ", "ɞ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ʎ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""), + 'rightalt-shift': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ɶ", "ʁ", "", "", "ᵾ", "ᵼ", "ɤ", "", "", "", "", "", "", "", "ᴂ", "", "", "", "ʛ", "ɧ", "", "", "ɺ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "") + } + }; + this.KH = ''; + this.KM = 0; + + this.dfltCodes = ["K_BKQUOTE","K_1","K_2","K_3","K_4","K_5","K_6","K_7","K_8","K_9","K_0", + "K_HYPHEN","K_EQUAL","K_*","K_*","K_*","K_Q","K_W","K_E","K_R","K_T", + "K_Y","K_U","K_I","K_O","K_P","K_LBRKT","K_RBRKT","K_BKSLASH","K_*", + "K_*","K_*","K_A","K_S","K_D","K_F","K_G","K_H","K_J","K_K","K_L", + "K_COLON","K_QUOTE","K_*","K_*","K_*","K_*","K_*","K_oE2", + "K_Z","K_X","K_C","K_V","K_B","K_N","K_M","K_COMMA","K_PERIOD", + "K_SLASH","K_*","K_*","K_*","K_*","K_*","K_SPACE"]; + + this.gs = function (t, e) { + return this.g0(t, e); + }; + this.g0 = function (t, e) { + var k = KeymanWeb, r = 0, m = 0; + + // Handwritten time! + var kls = this.KV.KLS; + + var layers = ['default', 'shift', 'leftctrl', 'leftctrl-shift', 'leftalt', 'rightalt', 'leftalt-shift', 'rightalt-shift']; + + // Maps keystrokes by base key-codes and array into the key symbols displayed in KLS. + for(var i = 0; i < layers.length; i++) { + // Obtain the modifier code to match for the selected layer. + var modCode = k.osk.modifierCodes['VIRTUAL_KEY'] | k.osk.getModifierState(layers[i]); + var layer = layers[i]; + + for(var key=0; key < kls[layer].length; key++) { + var keySymbol = this.dfltCodes[key]; + + if(keySymbol == "K_*") { + continue; + } else if(kls[layer][key] != '') { + if (k.KKM(e, modCode, k.osk.keyCodes[keySymbol])) { + r = m = 1; + if(k.KSM(e, k.osk.modifierCodes['CAPS'])) { + k.KO(0, t, kls[layer][key].toUpperCase()); + } else { + k.KO(0, t, kls[layer][key]); + } + return r; + } + } + } + } + + return r; + }; +} \ No newline at end of file diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java index ea21b206cf..071c9ed713 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java @@ -9,6 +9,8 @@ import android.util.Log; import android.content.Context; import android.view.KeyEvent; +import java.util.HashMap; + public class KMHardwareKeyboardInterpreter implements KeyEvent.Callback { /** @@ -114,18 +116,75 @@ public class KMHardwareKeyboardInterpreter implements KeyEvent.Callback { @Override public boolean onKeyDown(int keyCode, KeyEvent event) { + Log.d("onKeyDown", "keyCode " + String.valueOf(keyCode)); + Log.d("onKeyDown", "keyEvent.meta " + Integer.toHexString(event.getMetaState())); + + // Note: Keep this table in sync with kmwosk.js osk.modifierCodes + final HashMap modifierCodes = new HashMap() {{ + put("LCTRL", 0x0001); + put("RCTRL", 0x0002); + put("LALT", 0x0004); + put("RALT", 0x0008); + put("SHIFT", 0x0010); + put("CTRL", 0x0020); + put("ALT", 0x0040); + put("CAPS", 0x0100); + put("NO_CAPS", 0x0200); + put("NUM_LOCK", 0x0400); + put("NO_NUM_LOCK", 0x0800); + put("SCROLL_LOCK", 0x1000); + put("NO_SCROLL_LOCK", 0x2000); + put("VIRTUAL_KEY", 0x4000); + }}; + if (keyCode > 84 || keyCode < 0) { // The key is outside the range of keys we understand return false; } - int androidModifiers = event.getModifiers(), keymanModifiers = 0; - if ((androidModifiers & KeyEvent.META_SHIFT_ON) != 0) keymanModifiers |= 0x10; - if ((androidModifiers & KeyEvent.META_CTRL_ON) != 0) keymanModifiers |= 0x20; - if ((androidModifiers & KeyEvent.META_ALT_ON) != 0) keymanModifiers |= 0x40; + boolean isChiral = true; // TODO get from KMManager / KMKeyboard + /* + if (this.keyboardType == KMManager.KeyboardType.KEYBOARD_TYPE_INAPP) { + isChiral = KMManager.In + } + */ - if (keyCode == KeyEvent.KEYCODE_TAB && keymanModifiers == 0x20) { - // Trigger the Keyman language menu + // States of modifier keys + // KeyEvent.getModifiers() specifically masks out lock keys (KeyEvent.META_CAPS_LOCK_ON, + // KeyEvent.META_SCROLL_LOCK_ON, KeyEvent.META_NUM_LOCK_ON), so get their states separately + int androidModifiers = event.getModifiers(), keymanModifiers = 0; + boolean capsOn = event.isCapsLockOn(); + boolean numOn = event.isNumLockOn(); + boolean scrollOn = event.isScrollLockOn(); + + // By design, SHIFT is non-chiral + if ((androidModifiers & KeyEvent.META_SHIFT_ON) != 0) { + keymanModifiers |= modifierCodes.get("SHIFT"); + } + if ((androidModifiers & KeyEvent.META_CTRL_LEFT_ON) != 0) { + keymanModifiers |= isChiral ? modifierCodes.get("LCTRL") : modifierCodes.get("CTRL"); + } + if ((androidModifiers & KeyEvent.META_CTRL_RIGHT_ON) != 0) { + keymanModifiers |= isChiral ? modifierCodes.get("RCTRL") : modifierCodes.get("CTRL"); + } + if ((androidModifiers & KeyEvent.META_ALT_LEFT_ON) != 0) { + keymanModifiers |= isChiral ? modifierCodes.get("LALT") : modifierCodes.get("ALT"); + } + if ((androidModifiers & KeyEvent.META_ALT_RIGHT_ON) != 0) { + keymanModifiers |= isChiral ? modifierCodes.get("RALT") : modifierCodes.get("ALT"); + } + + int Lstates = 0; + Lstates |= capsOn ? modifierCodes.get("CAPS") : modifierCodes.get("NO_CAPS"); + Lstates |= numOn ? modifierCodes.get("NUM_LOCK") : modifierCodes.get("NO_NUM_LOCK"); + Lstates |= scrollOn ? modifierCodes.get("SCROLL_LOCK") : modifierCodes.get("NO_SCROLL_LOCK"); + + Log.d("onKeyDown", "androidModifier 0x" + Integer.toHexString(androidModifiers)); + Log.d("onKeyDown", "capsOn " + String.valueOf(capsOn) + " ; numOn " + String.valueOf(numOn) + "; scrollOn " + String.valueOf(scrollOn)); + Log.d("onKeyDown", "keymanModfiers 0x" + Integer.toHexString(keymanModifiers) + ", Lstates 0x" + Integer.toHexString(Lstates)); + + // CTRL-Tab triggers the Keyman language menu + if (keyCode == KeyEvent.KEYCODE_TAB && ((androidModifiers & KeyEvent.META_CTRL_ON) != 0)) { KMManager.showKeyboardPicker(context, keyboardType); return false; } @@ -136,7 +195,7 @@ public class KMHardwareKeyboardInterpreter implements KeyEvent.Callback { } // Send keystroke to KeymanWeb for processing: will return true to swallow the keystroke - return KMManager.executeHardwareKeystroke(keyCodeMap[keyCode], keymanModifiers, keyboardType); + return KMManager.executeHardwareKeystroke(keyCodeMap[keyCode], keymanModifiers, keyboardType, Lstates); } @Override diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMKeyboard.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMKeyboard.java index eee996561a..a8417232a8 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMKeyboard.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMKeyboard.java @@ -65,6 +65,7 @@ final class KMKeyboard extends WebView { public ArrayList> subKeysList = null; public String[] subKeysWindowPos = {"0", "0"}; public String specialOskFont = ""; + public boolean isChiral = false; public KMKeyboard(Context context) { super(context); @@ -138,9 +139,9 @@ final class KMKeyboard extends WebView { setBackgroundColor(0); } - public void executeHardwareKeystroke(int code, int shift) { - String jsFormat = "javascript:executeHardwareKeystroke(%d,%d)"; - String jsString = String.format(jsFormat, code, shift); + public void executeHardwareKeystroke(int code, int shift, int lstate) { + String jsFormat = "javascript:executeHardwareKeystroke(%d,%d, %d)"; + String jsString = String.format(jsFormat, code, shift, lstate); loadUrl(jsString); } diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java index 5bfc4c8eca..098995823c 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java @@ -125,6 +125,7 @@ public final class KMManager { public static final String KMKey_Filename = "filename"; public static final String KMKey_KeyboardModified = "lastModified"; public static final String KMKey_KeyboardRTL = "rtl"; + public static final String KMKey_Chirality = "chirality"; public static final String KMKey_CustomKeyboard = "CustomKeyboard"; public static final String KMKey_CustomHelpLink = "CustomHelpLink"; public static final String KMKey_UserKeyboardIndex = "UserKeyboardIndex"; @@ -140,6 +141,7 @@ public final class KMManager { public static final String KMDefault_KeyboardName = "EuroLatin2 Keyboard"; public static final String KMDefault_LanguageName = "English"; public static final String KMDefault_KeyboardFont = "{\"family\":\"LatinWeb\",\"source\":[\"DejaVuSans.ttf\"]}"; + public static final String KMDefault_Chirality = "false"; // Keyman files protected static final String KMFilename_KeyboardHtml = "keyboard.html"; @@ -178,22 +180,23 @@ public final class KMManager { IMService = service; } - public static boolean executeHardwareKeystroke(int code, int shift) { + public static boolean executeHardwareKeystroke(int code, int shift, int lstates) { if (SystemKeyboard != null) { - return executeHardwareKeystroke(code, shift, KeyboardType.KEYBOARD_TYPE_SYSTEM); + return executeHardwareKeystroke(code, shift, KeyboardType.KEYBOARD_TYPE_SYSTEM, lstates); } else if (InAppKeyboard != null) { - return executeHardwareKeystroke(code, shift, KeyboardType.KEYBOARD_TYPE_INAPP); + return executeHardwareKeystroke(code, shift, KeyboardType.KEYBOARD_TYPE_INAPP, lstates); } return false; } - public static boolean executeHardwareKeystroke(int code, int shift, KeyboardType keyboard) { + public static boolean executeHardwareKeystroke( + int code, int shift, KeyboardType keyboard, int lstates) { if (keyboard == KeyboardType.KEYBOARD_TYPE_INAPP) { - InAppKeyboard.executeHardwareKeystroke(code, shift); + InAppKeyboard.executeHardwareKeystroke(code, shift, lstates); return true; } else if (keyboard == KeyboardType.KEYBOARD_TYPE_SYSTEM) { - SystemKeyboard.executeHardwareKeystroke(code, shift); + SystemKeyboard.executeHardwareKeystroke(code, shift, lstates); return true; } @@ -214,6 +217,20 @@ public final class KMManager { InAppKeyboard.setWebViewClient(new KMInAppKeyboardWebViewClient(appContext)); InAppKeyboard.addJavascriptInterface(new KMInAppKeyboardJSHandler(appContext), "jsInterface"); InAppKeyboard.loadKeyboard(); + + // TODO: Test code adding chirality keyboard that needs to be removed before merging + HashMap kbInfo = new HashMap(); + final String Chirality_KeyboardFont = "{\"family\":\"LatinWeb\",\"source\":[\"DejaVuSans.ttf\"]}"; + + kbInfo.put(KMManager.KMKey_KeyboardID, "chirality"); + kbInfo.put(KMManager.KMKey_LanguageID, "eng"); + kbInfo.put(KMManager.KMKey_KeyboardName, "Chirality Keyboard"); + kbInfo.put(KMManager.KMKey_LanguageName, "English"); + kbInfo.put(KMManager.KMKey_KeyboardVersion, "1.0"); + kbInfo.put(KMManager.KMKey_Font, Chirality_KeyboardFont); + kbInfo.put(KMManager.KMKey_Chirality, "true"); + addKeyboard(appContext, kbInfo); + // end of test code } } @@ -454,9 +471,11 @@ public final class KMManager { newKbInfo.put(KMManager.KMKey_LanguageID, KMManager.KMDefault_LanguageID); newKbInfo.put(KMManager.KMKey_KeyboardName, KMManager.KMDefault_KeyboardName); newKbInfo.put(KMManager.KMKey_LanguageName, KMManager.KMDefault_LanguageName); - newKbInfo.put(KMManager.KMKey_KeyboardVersion, getLatestKeyboardFileVersion(context, KMManager.KMDefault_KeyboardID)); + newKbInfo.put(KMManager.KMKey_KeyboardVersion, + getLatestKeyboardFileVersion(context, KMManager.KMDefault_KeyboardID)); newKbInfo.put(KMManager.KMKey_CustomKeyboard, "N"); newKbInfo.put(KMManager.KMKey_Font, KMManager.KMDefault_KeyboardFont); + newKbInfo.put(KMManager.KMKey_Chirality, KMDefault_Chirality); kbList.set(0, newKbInfo); shouldUpdateList = true; shouldClearCache = true; diff --git a/android/history.md b/android/history.md index 1792b1f15a..17a4c4a9c4 100644 --- a/android/history.md +++ b/android/history.md @@ -1,6 +1,7 @@ # Keyman for Android ## 10.0 alpha +* Added support for L/R Alt and Ctrl and Caps Lock modifiers for keyboards if specified by a keyboard designer * Add feature to reset keyboard to default layer when new input field focused (keymanapp/keyman#288) * Removed "Share to Facebook" feature (keymanapp/keyman#156) * Fix dual keyboards that appear when closing Keyman Browser (keymanapp/keyman#220) diff --git a/web/source/kmwembedded.js b/web/source/kmwembedded.js index ee0a960950..469c9b9a36 100644 --- a/web/source/kmwembedded.js +++ b/web/source/kmwembedded.js @@ -456,8 +456,9 @@ * * @param {number} code key identifier * @param {number} shift shift state (0x10=shift 0x20=ctrl 0x40=alt) + * @param {number} lstate lock state (caps, num, scroll locks) **/ - keymanweb['executeHardwareKeystroke'] = function(code, shift) { + keymanweb['executeHardwareKeystroke'] = function(code, shift, lstate = 0) { if(!keymanweb._ActiveKeyboard || code == 0) { return false; } @@ -466,7 +467,7 @@ device.touchable = false; device.formFactor = 'desktop'; try { - result = keymanweb.executeHardwareKeystrokeInternal(code, shift); + result = keymanweb.executeHardwareKeystrokeInternal(code, shift, lstate); } catch (err) { console.error(err.message, err); } @@ -480,8 +481,9 @@ * * @param {number} code key identifier * @param {number} shift shift state (0x10=shift 0x20=ctrl 0x40=alt) + * @param {number} lstates lock state (caps, scroll, num locks) **/ - keymanweb.executeHardwareKeystrokeInternal = function(code, shift) { + keymanweb.executeHardwareKeystrokeInternal = function(code, shift, lstates) { // Clear any pending (non-popup) key osk.keyPending = null; @@ -498,6 +500,7 @@ Lmodifiers: shift, vkCode: code, Lcode: code, + Lstates: lstates, LisVirtualKey: true, LisVirtualKeyCode: false }; From df23e41eb143a1d3c7207913cdab765494876ad8 Mon Sep 17 00:00:00 2001 From: darcywong00 Date: Fri, 15 Sep 2017 14:41:58 +0700 Subject: [PATCH 02/19] Add hooks for KMManager to check if keyboard is chiral --- .../KMEA/app/src/main/assets/keyboard.html | 12 +++++-- .../kmea/KMHardwareKeyboardInterpreter.java | 23 ++++++------ .../java/com/tavultesoft/kmea/KMKeyboard.java | 35 +++++++++++++++++-- .../java/com/tavultesoft/kmea/KMManager.java | 20 ++++++++--- 4 files changed, 70 insertions(+), 20 deletions(-) diff --git a/android/KMEA/app/src/main/assets/keyboard.html b/android/KMEA/app/src/main/assets/keyboard.html index c21c714be7..da6e5d76fc 100644 --- a/android/KMEA/app/src/main/assets/keyboard.html +++ b/android/KMEA/app/src/main/assets/keyboard.html @@ -56,8 +56,16 @@ return oskWidth; } - function isChiral() { - return tavultesoft.keymanweb.isChiral(); + function setIsChiral(m) { + var isChiral = false; + if (typeof m !== "undefined") { + isChiral = tavultesoft.keymanweb.isChiral(m); + window.console.log('Keyboard ('+m+') isChiral = '+isChiral); + } else { + isChiral = tavultesoft.keymanweb.isChiral(); + window.console.log('Keyboard () isChiral = '+isChiral); + } + window.jsInterface.setIsChiral(isChiral); } function setKeymanLanguage(keyboardName, internalName, languageName, langId, version, font, oskFont) { diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java index 071c9ed713..5c35478a5b 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java @@ -116,9 +116,6 @@ public class KMHardwareKeyboardInterpreter implements KeyEvent.Callback { @Override public boolean onKeyDown(int keyCode, KeyEvent event) { - Log.d("onKeyDown", "keyCode " + String.valueOf(keyCode)); - Log.d("onKeyDown", "keyEvent.meta " + Integer.toHexString(event.getMetaState())); - // Note: Keep this table in sync with kmwosk.js osk.modifierCodes final HashMap modifierCodes = new HashMap() {{ put("LCTRL", 0x0001); @@ -142,12 +139,14 @@ public class KMHardwareKeyboardInterpreter implements KeyEvent.Callback { return false; } - boolean isChiral = true; // TODO get from KMManager / KMKeyboard - /* + boolean isChiral = false; if (this.keyboardType == KMManager.KeyboardType.KEYBOARD_TYPE_INAPP) { - isChiral = KMManager.In + isChiral = KMManager.InAppKeyboard.getChirality(); + } else if (this.keyboardType == KMManager.KeyboardType.KEYBOARD_TYPE_SYSTEM) { + isChiral = KMManager.SystemKeyboard.getChirality(); } - */ + Log.d("onKeyDown", "keyCode " + String.valueOf(keyCode) + "; keyEvent.meta " + + Integer.toHexString(event.getMetaState()) + "; isChiral " + String.valueOf(isChiral)); // States of modifier keys // KeyEvent.getModifiers() specifically masks out lock keys (KeyEvent.META_CAPS_LOCK_ON, @@ -174,11 +173,13 @@ public class KMHardwareKeyboardInterpreter implements KeyEvent.Callback { keymanModifiers |= isChiral ? modifierCodes.get("RALT") : modifierCodes.get("ALT"); } + // TODO : How to handle Caps lock for non-chiral keyboards? int Lstates = 0; - Lstates |= capsOn ? modifierCodes.get("CAPS") : modifierCodes.get("NO_CAPS"); - Lstates |= numOn ? modifierCodes.get("NUM_LOCK") : modifierCodes.get("NO_NUM_LOCK"); - Lstates |= scrollOn ? modifierCodes.get("SCROLL_LOCK") : modifierCodes.get("NO_SCROLL_LOCK"); - + if (isChiral) { + Lstates |= capsOn ? modifierCodes.get("CAPS") : modifierCodes.get("NO_CAPS"); + Lstates |= numOn ? modifierCodes.get("NUM_LOCK") : modifierCodes.get("NO_NUM_LOCK"); + Lstates |= scrollOn ? modifierCodes.get("SCROLL_LOCK") : modifierCodes.get("NO_SCROLL_LOCK"); + } Log.d("onKeyDown", "androidModifier 0x" + Integer.toHexString(androidModifiers)); Log.d("onKeyDown", "capsOn " + String.valueOf(capsOn) + " ; numOn " + String.valueOf(numOn) + "; scrollOn " + String.valueOf(scrollOn)); Log.d("onKeyDown", "keymanModfiers 0x" + Integer.toHexString(keymanModifiers) + ", Lstates 0x" + Integer.toHexString(Lstates)); diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMKeyboard.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMKeyboard.java index a8417232a8..40a3c10026 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMKeyboard.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMKeyboard.java @@ -54,6 +54,7 @@ final class KMKeyboard extends WebView { private GestureDetector gestureDetector; private static ArrayList kbEventListeners = null; private boolean ShouldShowHelpBubble = false; + private boolean isChiral = false; protected boolean keyboardSet = false; protected boolean keyboardPickerEnabled = true; @@ -65,7 +66,6 @@ final class KMKeyboard extends WebView { public ArrayList> subKeysList = null; public String[] subKeysWindowPos = {"0", "0"}; public String specialOskFont = ""; - public boolean isChiral = false; public KMKeyboard(Context context) { super(context); @@ -139,9 +139,9 @@ final class KMKeyboard extends WebView { setBackgroundColor(0); } - public void executeHardwareKeystroke(int code, int shift, int lstate) { + public void executeHardwareKeystroke(int code, int shift, int lstates) { String jsFormat = "javascript:executeHardwareKeystroke(%d,%d, %d)"; - String jsString = String.format(jsFormat, code, shift, lstate); + String jsString = String.format(jsFormat, code, shift, lstates); loadUrl(jsString); } @@ -298,6 +298,16 @@ final class KMKeyboard extends WebView { Log.d("KMKeyboard", jsString); } + Log.d("KMKeyboard", "1"); + // Add a little delay for the keyboard to load so the chirality can be properly detected + Handler chiralityHandler = new Handler(); + chiralityHandler.postDelayed(new Runnable() { + @Override + public void run() { + loadUrl("javascript:setIsChiral()"); + } + }, 1000); + currentKeyboard = kbKey; keyboardSet = true; saveCurrentKeyboardIndex(); @@ -389,6 +399,16 @@ final class KMKeyboard extends WebView { Log.d("KMKeyboard", jsString); } + Log.d("KMKeyboard", "2"); + // Add a little delay for the keyboard to load so the chirality can be properly detected + Handler chiralityHandler = new Handler(); + chiralityHandler.postDelayed(new Runnable() { + @Override + public void run() { + loadUrl("javascript:setIsChiral()"); + } + }, 1000); + currentKeyboard = kbKey; keyboardSet = true; saveCurrentKeyboardIndex(); @@ -407,6 +427,15 @@ final class KMKeyboard extends WebView { return retVal; } + public void setChirality(boolean flag) { + Log.d("KMKeyboard", "setChirality(" + String.valueOf(flag) + ") called"); + this.isChiral = flag; + } + + public boolean getChirality() { + return this.isChiral; + } + private void saveCurrentKeyboardIndex() { SharedPreferences prefs = context.getSharedPreferences(context.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java index 098995823c..fef2e2370d 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java @@ -125,7 +125,6 @@ public final class KMManager { public static final String KMKey_Filename = "filename"; public static final String KMKey_KeyboardModified = "lastModified"; public static final String KMKey_KeyboardRTL = "rtl"; - public static final String KMKey_Chirality = "chirality"; public static final String KMKey_CustomKeyboard = "CustomKeyboard"; public static final String KMKey_CustomHelpLink = "CustomHelpLink"; public static final String KMKey_UserKeyboardIndex = "UserKeyboardIndex"; @@ -141,7 +140,6 @@ public final class KMManager { public static final String KMDefault_KeyboardName = "EuroLatin2 Keyboard"; public static final String KMDefault_LanguageName = "English"; public static final String KMDefault_KeyboardFont = "{\"family\":\"LatinWeb\",\"source\":[\"DejaVuSans.ttf\"]}"; - public static final String KMDefault_Chirality = "false"; // Keyman files protected static final String KMFilename_KeyboardHtml = "keyboard.html"; @@ -228,8 +226,8 @@ public final class KMManager { kbInfo.put(KMManager.KMKey_LanguageName, "English"); kbInfo.put(KMManager.KMKey_KeyboardVersion, "1.0"); kbInfo.put(KMManager.KMKey_Font, Chirality_KeyboardFont); - kbInfo.put(KMManager.KMKey_Chirality, "true"); addKeyboard(appContext, kbInfo); + Log.d("KMM", "initInApp"); // end of test code } } @@ -248,6 +246,7 @@ public final class KMManager { SystemKeyboard.setWebViewClient(new KMSystemKeyboardWebViewClient(appContext)); SystemKeyboard.addJavascriptInterface(new KMSystemKeyboardJSHandler(appContext), "jsInterface"); SystemKeyboard.loadKeyboard(); + Log.d("KMM", "initSystem"); } } @@ -330,6 +329,7 @@ public final class KMManager { } public static void onConfigurationChanged(Configuration newConfig) { + Log.d("KMM", "onConfigChanged"); // KMKeyboard if (InAppKeyboard != null) { InAppKeyboard.onConfigurationChanged(newConfig); @@ -475,7 +475,6 @@ public final class KMManager { getLatestKeyboardFileVersion(context, KMManager.KMDefault_KeyboardID)); newKbInfo.put(KMManager.KMKey_CustomKeyboard, "N"); newKbInfo.put(KMManager.KMKey_Font, KMManager.KMDefault_KeyboardFont); - newKbInfo.put(KMManager.KMKey_Chirality, KMDefault_Chirality); kbList.set(0, newKbInfo); shouldUpdateList = true; shouldClearCache = true; @@ -1381,6 +1380,7 @@ public final class KMManager { } public static void switchToNextKeyboard(Context context) { + Log.d("KMM", "switchToNextKeyboard"); int index = KeyboardPickerActivity.getCurrentKeyboardIndex(context); index++; HashMap kbInfo = KeyboardPickerActivity.getKeyboardInfo(context, index); @@ -2275,6 +2275,12 @@ public final class KMManager { return kbWidth; } + // This annotation is required in Jelly Bean and later: + @JavascriptInterface + public void setIsChiral(boolean isChiral) { + InAppKeyboard.setChirality(isChiral); + } + // This annotation is required in Jelly Bean and later: @JavascriptInterface public void insertText(final int dn, final String s) { @@ -2380,6 +2386,12 @@ public final class KMManager { return kbWidth; } + // This annotation is required in Jelly Bean and later: + @JavascriptInterface + public void setIsChiral(boolean isChiral) { + SystemKeyboard.setChirality(isChiral); + } + // This annotation is required in Jelly Bean and later: @JavascriptInterface public void insertText(final int dn, final String s) { From 3a0ee923816b7bb32d24982b20da29fb1eaa9de6 Mon Sep 17 00:00:00 2001 From: darcywong00 Date: Mon, 25 Sep 2017 15:36:00 +0700 Subject: [PATCH 03/19] Move test chirality keyboard to new Tests/Chirality project -Update KMEA to copy keyman-engine.aar to Tests/ for future test apps to consume. --- android/.gitignore | 1 + .../KMEA/app/src/main/assets/keyboard.html | 2 - .../kmea/KMHardwareKeyboardInterpreter.java | 6 - .../java/com/tavultesoft/kmea/KMKeyboard.java | 3 - .../java/com/tavultesoft/kmea/KMManager.java | 17 -- android/KMEA/build.sh | 3 +- android/Tests/Chirality/app/.gitignore | 1 + android/Tests/Chirality/app/build.gradle | 33 ++++ android/Tests/Chirality/app/libs/.gitkeep | 0 .../Tests/Chirality/app/proguard-rules.pro | 17 ++ .../com/keyman/chirality/ApplicationTest.java | 13 ++ .../app/src/main/AndroidManifest.xml | 21 +++ .../src/main/assets/languages/chirality.js | 12 +- .../com/keyman/chirality/MainActivity.java | 120 +++++++++++++ .../app/src/main/res/layout/activity_main.xml | 20 +++ .../app/src/main/res/menu/menu_main.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3418 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2206 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4842 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 7718 bytes .../app/src/main/res/values-w820dp/dimens.xml | 6 + .../app/src/main/res/values/dimens.xml | 5 + .../app/src/main/res/values/strings.xml | 6 + .../app/src/main/res/values/styles.xml | 8 + android/Tests/Chirality/build.gradle | 19 ++ android/Tests/Chirality/build.sh | 11 ++ android/Tests/Chirality/gradle.properties | 18 ++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + android/Tests/Chirality/gradlew | 164 ++++++++++++++++++ android/Tests/Chirality/gradlew.bat | 90 ++++++++++ android/Tests/Chirality/settings.gradle | 1 + 32 files changed, 579 insertions(+), 30 deletions(-) create mode 100644 android/Tests/Chirality/app/.gitignore create mode 100644 android/Tests/Chirality/app/build.gradle create mode 100644 android/Tests/Chirality/app/libs/.gitkeep create mode 100644 android/Tests/Chirality/app/proguard-rules.pro create mode 100644 android/Tests/Chirality/app/src/androidTest/java/com/keyman/chirality/ApplicationTest.java create mode 100644 android/Tests/Chirality/app/src/main/AndroidManifest.xml rename android/{KMEA => Tests/Chirality}/app/src/main/assets/languages/chirality.js (86%) create mode 100644 android/Tests/Chirality/app/src/main/java/com/keyman/chirality/MainActivity.java create mode 100644 android/Tests/Chirality/app/src/main/res/layout/activity_main.xml create mode 100644 android/Tests/Chirality/app/src/main/res/menu/menu_main.xml create mode 100644 android/Tests/Chirality/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 android/Tests/Chirality/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 android/Tests/Chirality/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 android/Tests/Chirality/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 android/Tests/Chirality/app/src/main/res/values-w820dp/dimens.xml create mode 100644 android/Tests/Chirality/app/src/main/res/values/dimens.xml create mode 100644 android/Tests/Chirality/app/src/main/res/values/strings.xml create mode 100644 android/Tests/Chirality/app/src/main/res/values/styles.xml create mode 100644 android/Tests/Chirality/build.gradle create mode 100644 android/Tests/Chirality/build.sh create mode 100644 android/Tests/Chirality/gradle.properties create mode 100644 android/Tests/Chirality/gradle/wrapper/gradle-wrapper.jar create mode 100644 android/Tests/Chirality/gradle/wrapper/gradle-wrapper.properties create mode 100644 android/Tests/Chirality/gradlew create mode 100644 android/Tests/Chirality/gradlew.bat create mode 100644 android/Tests/Chirality/settings.gradle diff --git a/android/.gitignore b/android/.gitignore index 4bb6d1efb5..a04aad856f 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -21,6 +21,7 @@ # This is the file output from KMEA; it's part of our build process. **/libs/keyman-engine.aar +Tests/**/keyman-engine.aar # keymanweb dependencies KMEA/app/src/main/assets/ diff --git a/android/KMEA/app/src/main/assets/keyboard.html b/android/KMEA/app/src/main/assets/keyboard.html index da6e5d76fc..4fca9b6c8c 100644 --- a/android/KMEA/app/src/main/assets/keyboard.html +++ b/android/KMEA/app/src/main/assets/keyboard.html @@ -60,10 +60,8 @@ var isChiral = false; if (typeof m !== "undefined") { isChiral = tavultesoft.keymanweb.isChiral(m); - window.console.log('Keyboard ('+m+') isChiral = '+isChiral); } else { isChiral = tavultesoft.keymanweb.isChiral(); - window.console.log('Keyboard () isChiral = '+isChiral); } window.jsInterface.setIsChiral(isChiral); } diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java index 5c35478a5b..0ff4858bd5 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java @@ -145,8 +145,6 @@ public class KMHardwareKeyboardInterpreter implements KeyEvent.Callback { } else if (this.keyboardType == KMManager.KeyboardType.KEYBOARD_TYPE_SYSTEM) { isChiral = KMManager.SystemKeyboard.getChirality(); } - Log.d("onKeyDown", "keyCode " + String.valueOf(keyCode) + "; keyEvent.meta " + - Integer.toHexString(event.getMetaState()) + "; isChiral " + String.valueOf(isChiral)); // States of modifier keys // KeyEvent.getModifiers() specifically masks out lock keys (KeyEvent.META_CAPS_LOCK_ON, @@ -173,16 +171,12 @@ public class KMHardwareKeyboardInterpreter implements KeyEvent.Callback { keymanModifiers |= isChiral ? modifierCodes.get("RALT") : modifierCodes.get("ALT"); } - // TODO : How to handle Caps lock for non-chiral keyboards? int Lstates = 0; if (isChiral) { Lstates |= capsOn ? modifierCodes.get("CAPS") : modifierCodes.get("NO_CAPS"); Lstates |= numOn ? modifierCodes.get("NUM_LOCK") : modifierCodes.get("NO_NUM_LOCK"); Lstates |= scrollOn ? modifierCodes.get("SCROLL_LOCK") : modifierCodes.get("NO_SCROLL_LOCK"); } - Log.d("onKeyDown", "androidModifier 0x" + Integer.toHexString(androidModifiers)); - Log.d("onKeyDown", "capsOn " + String.valueOf(capsOn) + " ; numOn " + String.valueOf(numOn) + "; scrollOn " + String.valueOf(scrollOn)); - Log.d("onKeyDown", "keymanModfiers 0x" + Integer.toHexString(keymanModifiers) + ", Lstates 0x" + Integer.toHexString(Lstates)); // CTRL-Tab triggers the Keyman language menu if (keyCode == KeyEvent.KEYCODE_TAB && ((androidModifiers & KeyEvent.META_CTRL_ON) != 0)) { diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMKeyboard.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMKeyboard.java index 40a3c10026..2ba8ccf7e5 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMKeyboard.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMKeyboard.java @@ -298,7 +298,6 @@ final class KMKeyboard extends WebView { Log.d("KMKeyboard", jsString); } - Log.d("KMKeyboard", "1"); // Add a little delay for the keyboard to load so the chirality can be properly detected Handler chiralityHandler = new Handler(); chiralityHandler.postDelayed(new Runnable() { @@ -399,7 +398,6 @@ final class KMKeyboard extends WebView { Log.d("KMKeyboard", jsString); } - Log.d("KMKeyboard", "2"); // Add a little delay for the keyboard to load so the chirality can be properly detected Handler chiralityHandler = new Handler(); chiralityHandler.postDelayed(new Runnable() { @@ -428,7 +426,6 @@ final class KMKeyboard extends WebView { } public void setChirality(boolean flag) { - Log.d("KMKeyboard", "setChirality(" + String.valueOf(flag) + ") called"); this.isChiral = flag; } diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java index fef2e2370d..0ecd02e546 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java @@ -215,20 +215,6 @@ public final class KMManager { InAppKeyboard.setWebViewClient(new KMInAppKeyboardWebViewClient(appContext)); InAppKeyboard.addJavascriptInterface(new KMInAppKeyboardJSHandler(appContext), "jsInterface"); InAppKeyboard.loadKeyboard(); - - // TODO: Test code adding chirality keyboard that needs to be removed before merging - HashMap kbInfo = new HashMap(); - final String Chirality_KeyboardFont = "{\"family\":\"LatinWeb\",\"source\":[\"DejaVuSans.ttf\"]}"; - - kbInfo.put(KMManager.KMKey_KeyboardID, "chirality"); - kbInfo.put(KMManager.KMKey_LanguageID, "eng"); - kbInfo.put(KMManager.KMKey_KeyboardName, "Chirality Keyboard"); - kbInfo.put(KMManager.KMKey_LanguageName, "English"); - kbInfo.put(KMManager.KMKey_KeyboardVersion, "1.0"); - kbInfo.put(KMManager.KMKey_Font, Chirality_KeyboardFont); - addKeyboard(appContext, kbInfo); - Log.d("KMM", "initInApp"); - // end of test code } } @@ -246,7 +232,6 @@ public final class KMManager { SystemKeyboard.setWebViewClient(new KMSystemKeyboardWebViewClient(appContext)); SystemKeyboard.addJavascriptInterface(new KMSystemKeyboardJSHandler(appContext), "jsInterface"); SystemKeyboard.loadKeyboard(); - Log.d("KMM", "initSystem"); } } @@ -329,7 +314,6 @@ public final class KMManager { } public static void onConfigurationChanged(Configuration newConfig) { - Log.d("KMM", "onConfigChanged"); // KMKeyboard if (InAppKeyboard != null) { InAppKeyboard.onConfigurationChanged(newConfig); @@ -1380,7 +1364,6 @@ public final class KMManager { } public static void switchToNextKeyboard(Context context) { - Log.d("KMM", "switchToNextKeyboard"); int index = KeyboardPickerActivity.getCurrentKeyboardIndex(context); index++; HashMap kbInfo = KeyboardPickerActivity.getKeyboardInfo(context, index); diff --git a/android/KMEA/build.sh b/android/KMEA/build.sh index dc530c5e84..63ad831821 100755 --- a/android/KMEA/build.sh +++ b/android/KMEA/build.sh @@ -113,9 +113,10 @@ if [ $? -ne 0 ]; then die "ERROR: Build of KMEA failed" fi -echo "Copying Keyman Engine for Android to KMAPro and Sample apps" +echo "Copying Keyman Engine for Android to KMAPro, Sample apps, and Tests" mv $KMA_ROOT/KMEA/app/build/outputs/aar/app-release.aar $KMA_ROOT/KMAPro/kMAPro/libs/keyman-engine.aar cp $KMA_ROOT/KMAPro/kMAPro/libs/keyman-engine.aar $KMA_ROOT/Samples/KMSample1/app/libs/keyman-engine.aar cp $KMA_ROOT/KMAPro/kMAPro/libs/keyman-engine.aar $KMA_ROOT/Samples/KMSample2/app/libs/keyman-engine.aar +cp $KMA_ROOT/KMAPro/kMAPro/libs/keyman-engine.aar $KMA_ROOT/Tests/keyman-engine.aar cd ..\ diff --git a/android/Tests/Chirality/app/.gitignore b/android/Tests/Chirality/app/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/android/Tests/Chirality/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/android/Tests/Chirality/app/build.gradle b/android/Tests/Chirality/app/build.gradle new file mode 100644 index 0000000000..86387af459 --- /dev/null +++ b/android/Tests/Chirality/app/build.gradle @@ -0,0 +1,33 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 25 + buildToolsVersion "25.0.3" + + defaultConfig { + applicationId "com.keyman.chirality" + minSdkVersion 15 + targetSdkVersion 25 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +repositories { + mavenCentral() + flatDir { + dirs 'libs' + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'com.android.support:appcompat-v7:25.1.0' + compile(name:'keyman-engine', ext:'aar') +} diff --git a/android/Tests/Chirality/app/libs/.gitkeep b/android/Tests/Chirality/app/libs/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/android/Tests/Chirality/app/proguard-rules.pro b/android/Tests/Chirality/app/proguard-rules.pro new file mode 100644 index 0000000000..e0622e624f --- /dev/null +++ b/android/Tests/Chirality/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/serkankurt/Desktop/Android Development/adt-bundle-mac-x86_64-20140624/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/android/Tests/Chirality/app/src/androidTest/java/com/keyman/chirality/ApplicationTest.java b/android/Tests/Chirality/app/src/androidTest/java/com/keyman/chirality/ApplicationTest.java new file mode 100644 index 0000000000..a1f36a1cd0 --- /dev/null +++ b/android/Tests/Chirality/app/src/androidTest/java/com/keyman/chirality/ApplicationTest.java @@ -0,0 +1,13 @@ +package com.keyman.chirality; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/android/Tests/Chirality/app/src/main/AndroidManifest.xml b/android/Tests/Chirality/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..6c84f205e6 --- /dev/null +++ b/android/Tests/Chirality/app/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + diff --git a/android/KMEA/app/src/main/assets/languages/chirality.js b/android/Tests/Chirality/app/src/main/assets/languages/chirality.js similarity index 86% rename from android/KMEA/app/src/main/assets/languages/chirality.js rename to android/Tests/Chirality/app/src/main/assets/languages/chirality.js index 5f2f0075f1..ade1c50d02 100644 --- a/android/KMEA/app/src/main/assets/languages/chirality.js +++ b/android/Tests/Chirality/app/src/main/assets/languages/chirality.js @@ -1,8 +1,17 @@ KeymanWeb.KR(new Keyboard_chirality()); +/** + * Please note that this is an experimental handwritten keyboard designed for initial testing of modifier chirality support for KeymanWeb. + * As it is handcoded, it uses multiple codesize optimization techniques that Keyman Developer simply does not and will not employ, and it + * will only work with KeymanWeb based implementations. (Not supported for Keyman Desktop or Keyman for Mac.) + * + * (This keyboard was written before Developer's implementation of chirality support and was used to bootstrap KeymanWeb chirality, serving + * as the initial wave of testing.) + */ + function Keyboard_chirality() { this.KI = "Keyboard_chirality"; - this.KN = "Chirality Keyboard"; + this.KN = "Development Chirality Test Keyboard"; this.KV = { KMBM: 0x001F, F: ' 1em "Arial"', @@ -42,6 +51,7 @@ function Keyboard_chirality() { // Maps keystrokes by base key-codes and array into the key symbols displayed in KLS. for(var i = 0; i < layers.length; i++) { // Obtain the modifier code to match for the selected layer. + // The following uses a non-public property potentially subject to change in the future. var modCode = k.osk.modifierCodes['VIRTUAL_KEY'] | k.osk.getModifierState(layers[i]); var layer = layers[i]; diff --git a/android/Tests/Chirality/app/src/main/java/com/keyman/chirality/MainActivity.java b/android/Tests/Chirality/app/src/main/java/com/keyman/chirality/MainActivity.java new file mode 100644 index 0000000000..d26c637b5c --- /dev/null +++ b/android/Tests/Chirality/app/src/main/java/com/keyman/chirality/MainActivity.java @@ -0,0 +1,120 @@ +package com.keyman.chirality; + +import android.app.Activity; +import android.content.res.Configuration; +import android.os.Bundle; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; + +import com.tavultesoft.kmea.KMManager; +import com.tavultesoft.kmea.KMTextView; +import com.tavultesoft.kmea.KeyboardEventHandler.OnKeyboardEventListener; +import com.tavultesoft.kmea.KeyboardEventHandler.OnKeyboardDownloadEventListener; +import com.tavultesoft.kmea.KMManager.KeyboardType; + +import java.util.HashMap; + +public class MainActivity extends Activity implements OnKeyboardEventListener, OnKeyboardDownloadEventListener { + + private KMTextView textView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + KMManager.setDebugMode(true); + KMManager.initialize(this, KeyboardType.KEYBOARD_TYPE_INAPP); + + setContentView(R.layout.activity_main); + textView = (KMTextView) findViewById(R.id.kmTextView); + + // Add a custom keyboard + HashMap kbInfo = new HashMap(); + final String Chirality_KeyboardFont = "{\"family\":\"LatinWeb\",\"source\":[\"DejaVuSans.ttf\"]}"; + + kbInfo.put(KMManager.KMKey_KeyboardID, "chirality"); + kbInfo.put(KMManager.KMKey_LanguageID, "eng"); + kbInfo.put(KMManager.KMKey_KeyboardName, "Chirality Keyboard"); + kbInfo.put(KMManager.KMKey_LanguageName, "English"); + kbInfo.put(KMManager.KMKey_KeyboardVersion, "1.0"); + kbInfo.put(KMManager.KMKey_Font, Chirality_KeyboardFont); + KMManager.addKeyboard(this, kbInfo); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } + + @Override + protected void onResume() { + super.onResume(); + KMManager.onResume(); + KMManager.addKeyboardEventListener(this); + KMManager.addKeyboardDownloadEventListener(this); + } + + @Override + protected void onPause() { + super.onPause(); + KMManager.onPause(); + KMManager.removeKeyboardEventListener(this); + KMManager.removeKeyboardDownloadEventListener(this); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + } + + @Override + public void onKeyboardLoaded(KeyboardType keyboardType) { + // Handle Keyman keyboard loaded event here if needed + // We can set our custom keyboard here + int kbIndex = KMManager.getKeyboardIndex(this, "chirality", "eng"); + KMManager.setKeyboard(this, kbIndex); + } + + @Override + public void onKeyboardChanged(String newKeyboard) { + // Handle Keyman keyboard changed event here if needed + } + + @Override + public void onKeyboardShown() { + // Handle Keyman keyboard shown event here if needed + } + + @Override + public void onKeyboardDismissed() { + // Handle Keyman keyboard dismissed event here if needed + } + + @Override + public void onKeyboardDownloadStarted(HashMap keyboardInfo) { + // Handle Keyman keyboard download started event here if needed + } + + @Override + public void onKeyboardDownloadFinished(HashMap keyboardInfo, int result) { + // Handle Keyman keyboard download finished event here if needed + } +} diff --git a/android/Tests/Chirality/app/src/main/res/layout/activity_main.xml b/android/Tests/Chirality/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000000..317c86c320 --- /dev/null +++ b/android/Tests/Chirality/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/android/Tests/Chirality/app/src/main/res/menu/menu_main.xml b/android/Tests/Chirality/app/src/main/res/menu/menu_main.xml new file mode 100644 index 0000000000..b1cb90811d --- /dev/null +++ b/android/Tests/Chirality/app/src/main/res/menu/menu_main.xml @@ -0,0 +1,6 @@ + + + diff --git a/android/Tests/Chirality/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/Tests/Chirality/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..cde69bcccec65160d92116f20ffce4fce0b5245c GIT binary patch literal 3418 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 literal 0 HcmV?d00001 diff --git a/android/Tests/Chirality/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/Tests/Chirality/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..bfa42f0e7b91d006d22352c9ff2f134e504e3c1d GIT binary patch literal 4842 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 literal 0 HcmV?d00001 diff --git a/android/Tests/Chirality/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/Tests/Chirality/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..324e72cdd7480cb983fa1bcc7ce686e51ef87fe7 GIT binary patch literal 7718 zcmZ{JWl)?=u?hpbj?h-6mfK3P*Eck~k0Tzeg5-hkABxtZea0_k$f-mlF z0S@Qqtva`>x}TYzc}9LrO?P#qj+P1@HZ?W?0C;Muih9o&|G$cb@ocx1*PEUJ%~tM} z901hB;rx4#{@jOHs_MN00ADr$2n+#$yJuJ64gh!x0KlF(07#?(0ENrf7G3D`0EUHz zisCaq%dJ9dz%zhdRNuG*01nCjDhiPCl@b8xIMfv7^t~4jVRrSTGYyZUWqY@yW=)V_ z&3sUP1SK9v1f{4lDSN(agrKYULc;#EGDVeU*5b@#MOSY5JBn#QG8wqxQh+mdR638{mo5f>O zLUdZIPSjFk0~F26zDrM3y_#P^P91oWtLlPaZrhnM$NR%qsbHHK#?fN?cX?EvAhY1Sr9A(1;Kw4@87~|;2QP~ z(kKOGvCdB}qr4m#)1DwQFlh^NdBZvNLkld&yg%&GU`+boBMsoj5o?8tVuY^b0?4;E zsxoLxz8?S$y~a~x0{?dqk+6~Dd(EG7px_yH(X&NX&qEtHPUhu*JHD258=5$JS12rQ zcN+7p>R>tbFJ3NzEcRIpS98?}YEYxBIA8}1Y8zH9wq0c{hx+EXY&ZQ!-Hvy03X zLTMo4EZwtKfwb294-cY5XhQRxYJSybphcrNJWW2FY+b?|QB^?$5ZN=JlSs9Og(;8+ z*~-#CeeEOxt~F#aWn8wy-N_ilDDe_o+SwJD>4y?j5Lpj z2&!EX)RNxnadPBAa?fOj5D1C{l1E0X?&G3+ckcVfk`?%2FTsoUf4@~eaS#th=zq7v zMEJR@1T?Pi4;$xiPv`3)9rsrbVUH&b0e2{YTEG%;$GGzKUKEim;R6r>F@Q-}9JR-< zOPpQI>W0Vt6&7d?~$d&}chKTr_rELu} zWY;KTvtpJFr?P~ReHL4~2=ABn1`GN4Li%OI_1{mMRQi1Bf?+^Va?xdn4>h)Bq#ZRK zYo%R_h5etrv|!$1QF8fu80fN?1oXe(Jx#e6H^$+>C}N{*i$bNbELsXDA>cxlh|iFq zh~$yJ?1lTdcFd1Yv+Hr^PP!yupP!0H@Y6(wFcaVE+0?qjDJ1;*-Q8qL{NNPc{GAoi z_kBH`kw^(^7ShmzArk^A-!3_$W%!M-pGaZC=K`p-ch&iT%CV0>ofS74aPd7oT&cRr zXI30fVV6#PR*Z?c*orR0!$K6SUl9!H>hG+%`LdifNk`!Sw7Hon{Wn=|qV{a%v9nEq zAdBW*5kq6il=yA}x8cZQt^c+RBS|TRn;!?$ue?@jIV~0w1dt1FJRYI-K5>z-^01)R z)r}A&QXp^?-?}Uj`}ZPqB#}xO-?{0wrmi|eJOEjzdXbey4$rtKNHz)M*o?Ov+;S=K z-l~`)xV`%7Gvzy5wfvwqc0|80K29k0G~1nuBO+y-6)w11Kz2{>yD{HTt-uybe2pe? zUZK*Eij7TT4NwF1Jr@6R7gMuu^@qn#zPIgRtF?-SJL83LBDrh7k#{F^222EXPg}S0d4Lf0!|1 z|2k$^b~)^8$Z-yH{B-vo%7sVU@ZCvXN+Am)-fy$afZ_4HAUpK}j4p`UyXRel-+(VS z#K>-=-oA1pH+Lo$&|!lYB|M7Y&&bF##Oi@y_G3p1X$0I{jS1!NEdTz#x0`H`d*l%X z*8Y3>L*>j@ZQGOdPqwY(GzbA4nxqT(UAP<-tBf{_cb&Hn8hO5gEAotoV;tF6K4~wr2-M0v|2acQ!E@G*g$J z)~&_lvwN%WW>@U_taX5YX@a~pnG7A~jGwQwd4)QKk|^d_x9j+3JYmI5H`a)XMKwDt zk(nmso_I$Kc5m+8iVbIhY<4$34Oz!sg3oZF%UtS(sc6iq3?e8Z;P<{OFU9MACE6y( zeVprnhr!P;oc8pbE%A~S<+NGI2ZT@4A|o9bByQ0er$rYB3(c)7;=)^?$%a${0@70N zuiBVnAMd|qX7BE)8})+FAI&HM|BIb3e=e`b{Do8`J0jc$H>gl$zF26=haG31FDaep zd~i}CHSn$#8|WtE06vcA%1yxiy_TH|RmZ5>pI5*8pJZk0X54JDQQZgIf1Pp3*6hepV_cXe)L2iW$Ov=RZ4T)SP^a_8V} z+Nl?NJL7fAi<)Gt98U+LhE>x4W=bfo4F>5)qBx@^8&5-b>y*Wq19MyS(72ka8XFr2 zf*j(ExtQkjwN|4B?D z7+WzS*h6e_Po+Iqc-2n)gTz|de%FcTd_i9n+Y5*Vb=E{8xj&|h`CcUC*(yeCf~#Mf zzb-_ji&PNcctK6Xhe#gB0skjFFK5C4=k%tQQ}F|ZvEnPcH=#yH4n%z78?McMh!vek zVzwC0*OpmW2*-A6xz0=pE#WdXHMNxSJ*qGY(RoV9)|eu)HSSi_+|)IgT|!7HRx~ zjM$zp%LEBY)1AKKNI?~*>9DE3Y2t5p#jeqeq`1 zsjA-8eQKC*!$%k#=&jm+JG?UD(}M!tI{wD*3FQFt8jgv2xrRUJ}t}rWx2>XWz9ndH*cxl()ZC zoq?di!h6HY$fsglgay7|b6$cUG-f!U4blbj(rpP^1ZhHv@Oi~;BBvrv<+uC;%6QK!nyQ!bb3i3D~cvnpDAo3*3 zXRfZ@$J{FP?jf(NY7~-%Kem>jzZ2+LtbG!9I_fdJdD*;^T9gaiY>d+S$EdQrW9W62 z6w8M&v*8VWD_j)fmt?+bdavPn>oW8djd zRnQ}{XsIlwYWPp;GWLXvbSZ8#w25z1T}!<{_~(dcR_i1U?hyAe+lL*(Y6c;j2q7l! zMeN(nuA8Z9$#w2%ETSLjF{A#kE#WKus+%pal;-wx&tTsmFPOcbJtT?j&i(#-rB}l@ zXz|&%MXjD2YcYCZ3h4)?KnC*X$G%5N)1s!0!Ok!F9KLgV@wxMiFJIVH?E5JcwAnZF zU8ZPDJ_U_l81@&npI5WS7Y@_gf3vTXa;511h_(@{y1q-O{&bzJ z*8g>?c5=lUH6UfPj3=iuuHf4j?KJPq`x@en2Bp>#zIQjX5(C<9-X4X{a^S znWF1zJ=7rEUwQ&cZgyV4L12f&2^eIc^dGIJP@ToOgrU_Qe=T)utR;W$_2Vb7NiZ+d z$I0I>GFIutqOWiLmT~-Q<(?n5QaatHWj**>L8sxh1*pAkwG>siFMGEZYuZ)E!^Hfs zYBj`sbMQ5MR;6=1^0W*qO*Zthx-svsYqrUbJW)!vTGhWKGEu8c+=Yc%xi}Rncu3ph zTT1j_>={i3l#~$!rW!%ZtD9e6l6k-k8l{2w53!mmROAD^2yB^e)3f9_Qyf&C#zk`( z|5RL%r&}#t(;vF4nO&n}`iZpIL=p9tYtYv3%r@GzLWJ6%y_D(icSF^swYM`e8-n43iwo$C~>G<)dd0ze@5}n(!^YD zHf#OVbQ$Li@J}-qcOYn_iWF=_%)EXhrVuaYiai|B<1tXwNsow(m;XfL6^x~|Tr%L3~cs0@c) zDvOFU-AYn1!A;RBM0S}*EhYK49H$mBAxus)CB*KW(87#!#_C0wDr<0*dZ+GN&(3wR z6)cFLiDvOfs*-7Q75ekTAx)k!dtENUKHbP|2y4=tf*d_BeZ(9kR*m;dVzm&0fkKuD zVw5y9N>pz9C_wR+&Ql&&y{4@2M2?fWx~+>f|F%8E@fIfvSM$Dsk26(UL32oNvTR;M zE?F<7<;;jR4)ChzQaN((foV z)XqautTdMYtv<=oo-3W-t|gN7Q43N~%fnClny|NNcW9bIPPP5KK7_N8g!LB8{mK#! zH$74|$b4TAy@hAZ!;irT2?^B0kZ)7Dc?(7xawRUpO~AmA#}eX9A>+BA7{oDi)LA?F ze&CT`Cu_2=;8CWI)e~I_65cUmMPw5fqY1^6v))pc_TBArvAw_5Y8v0+fFFT`T zHP3&PYi2>CDO=a|@`asXnwe>W80%%<>JPo(DS}IQiBEBaNN0EF6HQ1L2i6GOPMOdN zjf3EMN!E(ceXhpd8~<6;6k<57OFRs;mpFM6VviPN>p3?NxrpNs0>K&nH_s ze)2#HhR9JHPAXf#viTkbc{-5C7U`N!`>J-$T!T6%=xo-)1_WO=+BG{J`iIk%tvxF39rJtK49Kj#ne;WG1JF1h7;~wauZ)nMvmBa2PPfrqREMKWX z@v}$0&+|nJrAAfRY-%?hS4+$B%DNMzBb_=Hl*i%euVLI5Ts~UsBVi(QHyKQ2LMXf` z0W+~Kz7$t#MuN|X2BJ(M=xZDRAyTLhPvC8i&9b=rS-T{k34X}|t+FMqf5gwQirD~N1!kK&^#+#8WvcfENOLA`Mcy@u~ zH10E=t+W=Q;gn}&;`R1D$n(8@Nd6f)9=F%l?A>?2w)H}O4avWOP@7IMVRjQ&aQDb) zzj{)MTY~Nk78>B!^EbpT{&h zy{wTABQlVVQG<4;UHY?;#Je#-E;cF3gVTx520^#XjvTlEX>+s{?KP#Rh@hM6R;~DE zaQY16$Axm5ycukte}4FtY-VZHc>=Ps8mJDLx3mwVvcF<^`Y6)v5tF`RMXhW1kE-;! z7~tpIQvz5a6~q-8@hTfF9`J;$QGQN%+VF#`>F4K3>h!tFU^L2jEagQ5Pk1U_I5&B> z+i<8EMFGFO$f7Z?pzI(jT0QkKnV)gw=j74h4*jfkk3UsUT5PemxD`pO^Y#~;P2Cte zzZ^pr>SQHC-576SI{p&FRy36<`&{Iej&&A&%>3-L{h(fUbGnb)*b&eaXj>i>gzllk zLXjw`pp#|yQIQ@;?mS=O-1Tj+ZLzy+aqr7%QwWl?j=*6dw5&4}>!wXqh&j%NuF{1q zzx$OXeWiAue+g#nkqQ#Uej@Zu;D+@z^VU*&HuNqqEm?V~(Z%7D`W5KSy^e|yF6kM7 z8Z9fEpcs^ElF9Vnolfs7^4b0fsNt+i?LwUX8Cv|iJeR|GOiFV!JyHdq+XQ&dER(KSqMxW{=M)lA?Exe&ZEB~6SmHg`zkcD7x#myq0h61+zhLr_NzEIjX zr~NGX_Uh~gdcrvjGI(&5K_zaEf}1t*)v3uT>~Gi$r^}R;H+0FEE5El{y;&DniH2@A z@!71_8mFHt1#V8MVsIYn={v&*0;3SWf4M$yLB^BdewOxz;Q=+gakk`S{_R_t!z2b| z+0d^C?G&7U6$_-W9@eR6SH%+qLx_Tf&Gu5%pn*mOGU0~kv~^K zhPeqYZMWWoA(Y+4GgQo9nNe6S#MZnyce_na@78ZnpwFenVafZC3N2lc5Jk-@V`{|l zhaF`zAL)+($xq8mFm{7fXtHru+DANoGz-A^1*@lTnE;1?03lz8kAnD{zQU=Pb^3f` zT5-g`z5|%qOa!WTBed-8`#AQ~wb9TrUZKU)H*O7!LtNnEd!r8!Oda)u!Gb5P`9(`b z`lMP6CLh4OzvXC#CR|@uo$EcHAyGr=)LB7)>=s3 zvU;aR#cN3<5&CLMFU@keW^R-Tqyf4fdkOnwI(H$x#@I1D6#dkUo@YW#7MU0@=NV-4 zEh2K?O@+2e{qW^7r?B~QTO)j}>hR$q9*n$8M(4+DOZ00WXFonLlk^;os8*zI>YG#? z9oq$CD~byz>;`--_NMy|iJRALZ#+qV8OXn=AmL^GL&|q1Qw-^*#~;WNNNbk(96Tnw zGjjscNyIyM2CYwiJ2l-}u_7mUGcvM+puPF^F89eIBx27&$|p_NG)fOaafGv|_b9G$;1LzZ-1aIE?*R6kHg}dy%~K(Q5S2O6086 z{lN&8;0>!pq^f*Jlh=J%Rmaoed<=uf@$iKl+bieC83IT!09J&IF)9H)C?d!eW1UQ}BQwxaqQY47DpOk@`zZ zo>#SM@oI^|nrWm~Ol7=r`!Bp9lQNbBCeHcfN&X$kjj0R(@?f$OHHt|fWe6jDrYg3(mdEd$8P2Yzjt9*EM zLE|cp-Tzsdyt(dvLhU8}_IX&I?B=|yoZ!&<`9&H5PtApt=VUIB4l0a1NH v0SQqt3DM`an1p};^>=lX|A*k@Y-MNT^ZzF}9G-1G696?OEyXH%^Pv9$0dR%J literal 0 HcmV?d00001 diff --git a/android/Tests/Chirality/app/src/main/res/values-w820dp/dimens.xml b/android/Tests/Chirality/app/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 0000000000..63fc816444 --- /dev/null +++ b/android/Tests/Chirality/app/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,6 @@ + + + 64dp + diff --git a/android/Tests/Chirality/app/src/main/res/values/dimens.xml b/android/Tests/Chirality/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000000..47c8224673 --- /dev/null +++ b/android/Tests/Chirality/app/src/main/res/values/dimens.xml @@ -0,0 +1,5 @@ + + + 16dp + 16dp + diff --git a/android/Tests/Chirality/app/src/main/res/values/strings.xml b/android/Tests/Chirality/app/src/main/res/values/strings.xml new file mode 100644 index 0000000000..4fec8f60da --- /dev/null +++ b/android/Tests/Chirality/app/src/main/res/values/strings.xml @@ -0,0 +1,6 @@ + + Chirality + + Hello world! + Settings + diff --git a/android/Tests/Chirality/app/src/main/res/values/styles.xml b/android/Tests/Chirality/app/src/main/res/values/styles.xml new file mode 100644 index 0000000000..e84bc5ec14 --- /dev/null +++ b/android/Tests/Chirality/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/android/Tests/Chirality/build.gradle b/android/Tests/Chirality/build.gradle new file mode 100644 index 0000000000..596601301b --- /dev/null +++ b/android/Tests/Chirality/build.gradle @@ -0,0 +1,19 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.3.3' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + jcenter() + } +} diff --git a/android/Tests/Chirality/build.sh b/android/Tests/Chirality/build.sh new file mode 100644 index 0000000000..7be5e8447b --- /dev/null +++ b/android/Tests/Chirality/build.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# Build Chirality + +echo Build KMEA +cd ../../KMEA +./build.sh +cd ../Tests/Chirality +cp ../keyman-engine.aar app/libs/ + +echo Build Chirality +./gradlew clean build diff --git a/android/Tests/Chirality/gradle.properties b/android/Tests/Chirality/gradle.properties new file mode 100644 index 0000000000..1d3591c8a4 --- /dev/null +++ b/android/Tests/Chirality/gradle.properties @@ -0,0 +1,18 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true \ No newline at end of file diff --git a/android/Tests/Chirality/gradle/wrapper/gradle-wrapper.jar b/android/Tests/Chirality/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..8c0fb64a8698b08ecc4158d828ca593c4928e9dd GIT binary patch literal 49896 zcmagFb986H(k`5d^NVfUwr$(C?M#x1ZQHiZiEVpg+jrjgoQrerx!>1o_ul)D>ebz~ zs=Mmxr&>W81QY-S1PKWQ%N-;H^tS;2*XwVA`dej1RRn1z<;3VgfE4~kaG`A%QSPsR z#ovnZe+tS9%1MfeDyz`RirvdjPRK~p(#^q2(^5@O&NM19EHdvN-A&StN>0g6QA^VN z0Gx%Gq#PD$QMRFzmK+utjS^Y1F0e8&u&^=w5K<;4Rz|i3A=o|IKLY+g`iK6vfr9?+ z-`>gmU&i?FGSL5&F?TXFu`&Js6h;15QFkXp2M1H9|Eq~bpov-GU(uz%mH0n55wUl- zv#~ccAz`F5wlQ>e_KlJS3@{)B?^v*EQM=IxLa&76^y51a((wq|2-`qON>+4dLc{Oo z51}}o^Zen(oAjxDK7b++9_Yg`67p$bPo3~BCpGM7uAWmvIhWc5Gi+gQZ|Pwa-Gll@<1xmcPy z|NZmu6m)g5Ftu~BG&Xdxclw7Cij{xbBMBn-LMII#Slp`AElb&2^Hw+w>(3crLH!;I zN+Vk$D+wP1#^!MDCiad@vM>H#6+`Ct#~6VHL4lzmy;lSdk>`z6)=>Wh15Q2)dQtGqvn0vJU@+(B5{MUc*qs4!T+V=q=wy)<6$~ z!G>e_4dN@lGeF_$q9`Ju6Ncb*x?O7=l{anm7Eahuj_6lA{*#Gv*TaJclevPVbbVYu z(NY?5q+xxbO6%g1xF0r@Ix8fJ~u)VRUp`S%&rN$&e!Od`~s+64J z5*)*WSi*i{k%JjMSIN#X;jC{HG$-^iX+5f5BGOIHWAl*%15Z#!xntpk($-EGKCzKa zT7{siZ9;4TICsWQ$pu&wKZQTCvpI$Xvzwxoi+XkkpeE&&kFb!B?h2hi%^YlXt|-@5 zHJ~%AN!g_^tmn1?HSm^|gCE#!GRtK2(L{9pL#hp0xh zME}|DB>(5)`iE7CM)&_+S}-Bslc#@B5W4_+k4Cp$l>iVyg$KP>CN?SVGZ(&02>iZK zB<^HP$g$Lq*L$BWd?2(F?-MUbNWTJVQdW7$#8a|k_30#vHAD1Z{c#p;bETk0VnU5A zBgLe2HFJ3032$G<`m*OB!KM$*sdM20jm)It5OSru@tXpK5LT>#8)N!*skNu1$TpIw zufjjdp#lyH5bZ%|Iuo|iu9vG1HrIVWLH>278xo>aVBkPN3V$~!=KnlXQ4eDqS7%E% zQ!z^$Q$b^6Q)g#cLpwur(|<0gWHo6A6jc;n`t(V9T;LzTAU{IAu*uEQ%Ort1k+Kn+f_N`9|bxYC+~Z1 zCC1UCWv*Orx$_@ydv9mIe(liLfOr7mhbV@tKw{6)q^1DH1nmvZ0cj215R<~&I<4S| zgnr;9Cdjqpz#o8i0CQjtl`}{c*P)aSdH|abxGdrR)-3z+02-eX(k*B)Uqv6~^nh** z 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/android/Tests/Chirality/gradlew.bat b/android/Tests/Chirality/gradlew.bat new file mode 100644 index 0000000000..8a0b282aa6 --- /dev/null +++ b/android/Tests/Chirality/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/android/Tests/Chirality/settings.gradle b/android/Tests/Chirality/settings.gradle new file mode 100644 index 0000000000..e7b4def49c --- /dev/null +++ b/android/Tests/Chirality/settings.gradle @@ -0,0 +1 @@ +include ':app' From cd59fe48c747cc48c1fd9cb9415408b61052e94c Mon Sep 17 00:00:00 2001 From: darcywong00 Date: Tue, 26 Sep 2017 16:25:31 +0700 Subject: [PATCH 04/19] Start addressing review comments Refactor KMManager.getActiveKeyboard() Move modifierCodes to separate class Clean up .gitignore --- android/.gitignore | 3 +- .../kmea/KMHardwareKeyboardInterpreter.java | 46 ++++--------------- .../java/com/tavultesoft/kmea/KMManager.java | 43 +++++++---------- .../com/tavultesoft/kmea/KMModifierCodes.java | 32 +++++++++++++ 4 files changed, 59 insertions(+), 65 deletions(-) create mode 100644 android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMModifierCodes.java diff --git a/android/.gitignore b/android/.gitignore index a04aad856f..d0dd22b727 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -20,8 +20,7 @@ **/.DS_Store # This is the file output from KMEA; it's part of our build process. -**/libs/keyman-engine.aar -Tests/**/keyman-engine.aar +**/keyman-engine.aar # keymanweb dependencies KMEA/app/src/main/assets/ diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java index 0ff4858bd5..ef9194c1b5 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java @@ -4,13 +4,9 @@ package com.tavultesoft.kmea; -import android.util.Log; - import android.content.Context; import android.view.KeyEvent; -import java.util.HashMap; - public class KMHardwareKeyboardInterpreter implements KeyEvent.Callback { /** @@ -116,35 +112,13 @@ public class KMHardwareKeyboardInterpreter implements KeyEvent.Callback { @Override public boolean onKeyDown(int keyCode, KeyEvent event) { - // Note: Keep this table in sync with kmwosk.js osk.modifierCodes - final HashMap modifierCodes = new HashMap() {{ - put("LCTRL", 0x0001); - put("RCTRL", 0x0002); - put("LALT", 0x0004); - put("RALT", 0x0008); - put("SHIFT", 0x0010); - put("CTRL", 0x0020); - put("ALT", 0x0040); - put("CAPS", 0x0100); - put("NO_CAPS", 0x0200); - put("NUM_LOCK", 0x0400); - put("NO_NUM_LOCK", 0x0800); - put("SCROLL_LOCK", 0x1000); - put("NO_SCROLL_LOCK", 0x2000); - put("VIRTUAL_KEY", 0x4000); - }}; if (keyCode > 84 || keyCode < 0) { // The key is outside the range of keys we understand return false; } - boolean isChiral = false; - if (this.keyboardType == KMManager.KeyboardType.KEYBOARD_TYPE_INAPP) { - isChiral = KMManager.InAppKeyboard.getChirality(); - } else if (this.keyboardType == KMManager.KeyboardType.KEYBOARD_TYPE_SYSTEM) { - isChiral = KMManager.SystemKeyboard.getChirality(); - } + boolean isChiral = KMManager.getActiveKeyboard().getChirality(); // States of modifier keys // KeyEvent.getModifiers() specifically masks out lock keys (KeyEvent.META_CAPS_LOCK_ON, @@ -156,27 +130,25 @@ public class KMHardwareKeyboardInterpreter implements KeyEvent.Callback { // By design, SHIFT is non-chiral if ((androidModifiers & KeyEvent.META_SHIFT_ON) != 0) { - keymanModifiers |= modifierCodes.get("SHIFT"); + keymanModifiers |= KMModifierCodes.get("SHIFT"); } if ((androidModifiers & KeyEvent.META_CTRL_LEFT_ON) != 0) { - keymanModifiers |= isChiral ? modifierCodes.get("LCTRL") : modifierCodes.get("CTRL"); + keymanModifiers |= isChiral ? KMModifierCodes.get("LCTRL") : KMModifierCodes.get("CTRL"); } if ((androidModifiers & KeyEvent.META_CTRL_RIGHT_ON) != 0) { - keymanModifiers |= isChiral ? modifierCodes.get("RCTRL") : modifierCodes.get("CTRL"); + keymanModifiers |= isChiral ? KMModifierCodes.get("RCTRL") : KMModifierCodes.get("CTRL"); } if ((androidModifiers & KeyEvent.META_ALT_LEFT_ON) != 0) { - keymanModifiers |= isChiral ? modifierCodes.get("LALT") : modifierCodes.get("ALT"); + keymanModifiers |= isChiral ? KMModifierCodes.get("LALT") : KMModifierCodes.get("ALT"); } if ((androidModifiers & KeyEvent.META_ALT_RIGHT_ON) != 0) { - keymanModifiers |= isChiral ? modifierCodes.get("RALT") : modifierCodes.get("ALT"); + keymanModifiers |= isChiral ? KMModifierCodes.get("RALT") : KMModifierCodes.get("ALT"); } int Lstates = 0; - if (isChiral) { - Lstates |= capsOn ? modifierCodes.get("CAPS") : modifierCodes.get("NO_CAPS"); - Lstates |= numOn ? modifierCodes.get("NUM_LOCK") : modifierCodes.get("NO_NUM_LOCK"); - Lstates |= scrollOn ? modifierCodes.get("SCROLL_LOCK") : modifierCodes.get("NO_SCROLL_LOCK"); - } + Lstates |= capsOn ? KMModifierCodes.get("CAPS") : KMModifierCodes.get("NO_CAPS"); + Lstates |= numOn ? KMModifierCodes.get("NUM_LOCK") : KMModifierCodes.get("NO_NUM_LOCK"); + Lstates |= scrollOn ? KMModifierCodes.get("SCROLL_LOCK") : KMModifierCodes.get("NO_SCROLL_LOCK"); // CTRL-Tab triggers the Keyman language menu if (keyCode == KeyEvent.KEYCODE_TAB && ((androidModifiers & KeyEvent.META_CTRL_ON) != 0)) { diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java index 0ecd02e546..d77cb74664 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java @@ -1160,10 +1160,7 @@ public final class KMManager { } if (result > 0) { - if (KMManager.InAppKeyboard != null) - KMManager.InAppKeyboard.loadKeyboard(); - if (KMManager.SystemKeyboard != null) - KMManager.SystemKeyboard.loadKeyboard(); + KMManager.getActiveKeyboard().loadKeyboard(); } } }.execute(); @@ -1378,10 +1375,20 @@ public final class KMManager { String langName = kbInfo.get(KMManager.KMKey_LanguageName); String kFont = kbInfo.get(KMManager.KMKey_Font); String kOskFont = kbInfo.get(KMManager.KMKey_OskFont); - if (InAppKeyboard != null) - InAppKeyboard.setKeyboard(kbId, langId, kbName, langName, kFont, kOskFont); - if (SystemKeyboard != null) - SystemKeyboard.setKeyboard(kbId, langId, kbName, langName, kFont, kOskFont); + KMManager.getActiveKeyboard().setKeyboard(kbId, langId, kbName, langName, kFont, kOskFont); + } + + public static KMKeyboard getActiveKeyboard() { + Log.d("KMM", "InApp: " + String.valueOf(InAppKeyboard != null) + " SystemKeyboard: " + String.valueOf(SystemKeyboard != null)); + Log.d("KMM", "InAppKeyboardLoaded: " + String.valueOf(InAppKeyboardLoaded) + " SystemKeyboardLoaded: " + String.valueOf(SystemKeyboardLoaded)); + if (InAppKeyboard != null && InAppKeyboardLoaded) { + return InAppKeyboard; + } else if (SystemKeyboard != null && SystemKeyboardLoaded) { + return SystemKeyboard; + } else { + Log.w("KMManager", "Cannot determine active keyboard"); + return null; + } } public static String getKeyboardTextFontFilename() { @@ -1779,25 +1786,11 @@ public final class KMManager { } public static boolean isHelpBubbleEnabled() { - boolean retVal = true; - - if (InAppKeyboard != null) { - retVal = InAppKeyboard.isHelpBubbleEnabled; - } else if (SystemKeyboard != null) { - retVal = SystemKeyboard.isHelpBubbleEnabled; - } - - return retVal; + return KMManager.getActiveKeyboard().isHelpBubbleEnabled; } public static void setHelpBubbleEnabled(boolean newValue) { - if (InAppKeyboard != null) { - InAppKeyboard.isHelpBubbleEnabled = newValue; - } - - if (SystemKeyboard != null) { - SystemKeyboard.isHelpBubbleEnabled = newValue; - } + KMManager.getActiveKeyboard().isHelpBubbleEnabled = newValue; } public static boolean canAddNewKeyboard() { @@ -2258,7 +2251,6 @@ public final class KMManager { return kbWidth; } - // This annotation is required in Jelly Bean and later: @JavascriptInterface public void setIsChiral(boolean isChiral) { InAppKeyboard.setChirality(isChiral); @@ -2369,7 +2361,6 @@ public final class KMManager { return kbWidth; } - // This annotation is required in Jelly Bean and later: @JavascriptInterface public void setIsChiral(boolean isChiral) { SystemKeyboard.setChirality(isChiral); diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMModifierCodes.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMModifierCodes.java new file mode 100644 index 0000000000..872e37d569 --- /dev/null +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMModifierCodes.java @@ -0,0 +1,32 @@ +package com.tavultesoft.kmea; + +import java.util.HashMap; + +public final class KMModifierCodes { + + // Note: Keep this table in sync with web/source/kmwosk.js osk.modifierCodes + final static HashMap codes = new HashMap() {{ + put("LCTRL", 0x0001); + put("RCTRL", 0x0002); + put("LALT", 0x0004); + put("RALT", 0x0008); + put("SHIFT", 0x0010); + put("CTRL", 0x0020); + put("ALT", 0x0040); + put("CAPS", 0x0100); + put("NO_CAPS", 0x0200); + put("NUM_LOCK", 0x0400); + put("NO_NUM_LOCK", 0x0800); + put("SCROLL_LOCK", 0x1000); + put("NO_SCROLL_LOCK", 0x2000); + put("VIRTUAL_KEY", 0x4000); + }}; + + public static Integer get(String key) { + Integer bitflag = codes.get(key); + if (bitflag == null) { + bitflag = 0x0; + } + return bitflag; + }; +} From 353f09084ea869910684e149c051b38afa4ed7fa Mon Sep 17 00:00:00 2001 From: darcywong00 Date: Tue, 26 Sep 2017 22:16:01 +0700 Subject: [PATCH 05/19] Change chirality test app to keyboardHarness Per review comments * Rename chirality to keyboardHarness * Change namespace to com.keyman.android.tests.keyboardHarness * Add readme.md for info --- .../{Chirality => KeyboardHarness}/app/.gitignore | 0 .../{Chirality => KeyboardHarness}/app/build.gradle | 2 +- .../app/libs/.gitkeep | 0 .../app/proguard-rules.pro | 0 .../tests/keyboardHarness}/ApplicationTest.java | 2 +- .../app/src/main/AndroidManifest.xml | 2 +- .../app/src/main/assets/languages/chirality.js | 0 .../tests/keyboardHarness}/MainActivity.java | 2 +- .../app/src/main/res/layout/activity_main.xml | 0 .../app/src/main/res/menu/menu_main.xml | 0 .../app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../app/src/main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../app/src/main/res/values-w820dp/dimens.xml | 0 .../app/src/main/res/values/dimens.xml | 0 .../app/src/main/res/values/strings.xml | 2 +- .../app/src/main/res/values/styles.xml | 0 .../{Chirality => KeyboardHarness}/build.gradle | 0 .../Tests/{Chirality => KeyboardHarness}/build.sh | 6 +++--- .../gradle.properties | 0 .../gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/wrapper/gradle-wrapper.properties | 0 .../Tests/{Chirality => KeyboardHarness}/gradlew | 0 .../{Chirality => KeyboardHarness}/gradlew.bat | 0 android/Tests/KeyboardHarness/readme.md | 9 +++++++++ .../{Chirality => KeyboardHarness}/settings.gradle | 0 27 files changed, 17 insertions(+), 8 deletions(-) rename android/Tests/{Chirality => KeyboardHarness}/app/.gitignore (100%) rename android/Tests/{Chirality => KeyboardHarness}/app/build.gradle (91%) rename android/Tests/{Chirality => KeyboardHarness}/app/libs/.gitkeep (100%) rename android/Tests/{Chirality => KeyboardHarness}/app/proguard-rules.pro (100%) rename android/Tests/{Chirality/app/src/androidTest/java/com/keyman/chirality => KeyboardHarness/app/src/androidTest/java/com/keyman/android/tests/keyboardHarness}/ApplicationTest.java (86%) rename android/Tests/{Chirality => KeyboardHarness}/app/src/main/AndroidManifest.xml (93%) rename android/Tests/{Chirality => KeyboardHarness}/app/src/main/assets/languages/chirality.js (100%) rename android/Tests/{Chirality/app/src/main/java/com/keyman/chirality => KeyboardHarness/app/src/main/java/com/keyman/android/tests/keyboardHarness}/MainActivity.java (98%) rename android/Tests/{Chirality => KeyboardHarness}/app/src/main/res/layout/activity_main.xml (100%) rename android/Tests/{Chirality => KeyboardHarness}/app/src/main/res/menu/menu_main.xml (100%) rename android/Tests/{Chirality => KeyboardHarness}/app/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename android/Tests/{Chirality => KeyboardHarness}/app/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename android/Tests/{Chirality => KeyboardHarness}/app/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename android/Tests/{Chirality => KeyboardHarness}/app/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename android/Tests/{Chirality => KeyboardHarness}/app/src/main/res/values-w820dp/dimens.xml (100%) rename android/Tests/{Chirality => KeyboardHarness}/app/src/main/res/values/dimens.xml (100%) rename android/Tests/{Chirality => KeyboardHarness}/app/src/main/res/values/strings.xml (71%) rename android/Tests/{Chirality => KeyboardHarness}/app/src/main/res/values/styles.xml (100%) rename android/Tests/{Chirality => KeyboardHarness}/build.gradle (100%) rename android/Tests/{Chirality => KeyboardHarness}/build.sh (52%) rename android/Tests/{Chirality => KeyboardHarness}/gradle.properties (100%) rename android/Tests/{Chirality => KeyboardHarness}/gradle/wrapper/gradle-wrapper.jar (100%) rename android/Tests/{Chirality => KeyboardHarness}/gradle/wrapper/gradle-wrapper.properties (100%) rename android/Tests/{Chirality => KeyboardHarness}/gradlew (100%) rename android/Tests/{Chirality => KeyboardHarness}/gradlew.bat (100%) create mode 100644 android/Tests/KeyboardHarness/readme.md rename android/Tests/{Chirality => KeyboardHarness}/settings.gradle (100%) diff --git a/android/Tests/Chirality/app/.gitignore b/android/Tests/KeyboardHarness/app/.gitignore similarity index 100% rename from android/Tests/Chirality/app/.gitignore rename to android/Tests/KeyboardHarness/app/.gitignore diff --git a/android/Tests/Chirality/app/build.gradle b/android/Tests/KeyboardHarness/app/build.gradle similarity index 91% rename from android/Tests/Chirality/app/build.gradle rename to android/Tests/KeyboardHarness/app/build.gradle index 86387af459..6ace5628d1 100644 --- a/android/Tests/Chirality/app/build.gradle +++ b/android/Tests/KeyboardHarness/app/build.gradle @@ -5,7 +5,7 @@ android { buildToolsVersion "25.0.3" defaultConfig { - applicationId "com.keyman.chirality" + applicationId "com.keyman.android.tests.keyboardHarness" minSdkVersion 15 targetSdkVersion 25 versionCode 1 diff --git a/android/Tests/Chirality/app/libs/.gitkeep b/android/Tests/KeyboardHarness/app/libs/.gitkeep similarity index 100% rename from android/Tests/Chirality/app/libs/.gitkeep rename to android/Tests/KeyboardHarness/app/libs/.gitkeep diff --git a/android/Tests/Chirality/app/proguard-rules.pro b/android/Tests/KeyboardHarness/app/proguard-rules.pro similarity index 100% rename from android/Tests/Chirality/app/proguard-rules.pro rename to android/Tests/KeyboardHarness/app/proguard-rules.pro diff --git a/android/Tests/Chirality/app/src/androidTest/java/com/keyman/chirality/ApplicationTest.java b/android/Tests/KeyboardHarness/app/src/androidTest/java/com/keyman/android/tests/keyboardHarness/ApplicationTest.java similarity index 86% rename from android/Tests/Chirality/app/src/androidTest/java/com/keyman/chirality/ApplicationTest.java rename to android/Tests/KeyboardHarness/app/src/androidTest/java/com/keyman/android/tests/keyboardHarness/ApplicationTest.java index a1f36a1cd0..25b8eea0cb 100644 --- a/android/Tests/Chirality/app/src/androidTest/java/com/keyman/chirality/ApplicationTest.java +++ b/android/Tests/KeyboardHarness/app/src/androidTest/java/com/keyman/android/tests/keyboardHarness/ApplicationTest.java @@ -1,4 +1,4 @@ -package com.keyman.chirality; +package com.keyman.android.tests.keyboardHarness; import android.app.Application; import android.test.ApplicationTestCase; diff --git a/android/Tests/Chirality/app/src/main/AndroidManifest.xml b/android/Tests/KeyboardHarness/app/src/main/AndroidManifest.xml similarity index 93% rename from android/Tests/Chirality/app/src/main/AndroidManifest.xml rename to android/Tests/KeyboardHarness/app/src/main/AndroidManifest.xml index 6c84f205e6..8ca7121ff2 100644 --- a/android/Tests/Chirality/app/src/main/AndroidManifest.xml +++ b/android/Tests/KeyboardHarness/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ + package="com.keyman.android.tests.keyboardHarness" > - Chirality + KeyboardHarness Hello world! Settings diff --git a/android/Tests/Chirality/app/src/main/res/values/styles.xml b/android/Tests/KeyboardHarness/app/src/main/res/values/styles.xml similarity index 100% rename from android/Tests/Chirality/app/src/main/res/values/styles.xml rename to android/Tests/KeyboardHarness/app/src/main/res/values/styles.xml diff --git a/android/Tests/Chirality/build.gradle b/android/Tests/KeyboardHarness/build.gradle similarity index 100% rename from android/Tests/Chirality/build.gradle rename to android/Tests/KeyboardHarness/build.gradle diff --git a/android/Tests/Chirality/build.sh b/android/Tests/KeyboardHarness/build.sh similarity index 52% rename from android/Tests/Chirality/build.sh rename to android/Tests/KeyboardHarness/build.sh index 7be5e8447b..2dbba42f4c 100644 --- a/android/Tests/Chirality/build.sh +++ b/android/Tests/KeyboardHarness/build.sh @@ -1,11 +1,11 @@ #!/bin/sh -# Build Chirality +# Build KeyboardHarness test app echo Build KMEA cd ../../KMEA ./build.sh -cd ../Tests/Chirality +cd ../Tests/KeyboardHarness cp ../keyman-engine.aar app/libs/ -echo Build Chirality +echo Build KeyboardHarness test app ./gradlew clean build diff --git a/android/Tests/Chirality/gradle.properties b/android/Tests/KeyboardHarness/gradle.properties similarity index 100% rename from android/Tests/Chirality/gradle.properties rename to android/Tests/KeyboardHarness/gradle.properties diff --git a/android/Tests/Chirality/gradle/wrapper/gradle-wrapper.jar b/android/Tests/KeyboardHarness/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from android/Tests/Chirality/gradle/wrapper/gradle-wrapper.jar rename to android/Tests/KeyboardHarness/gradle/wrapper/gradle-wrapper.jar diff --git a/android/Tests/Chirality/gradle/wrapper/gradle-wrapper.properties b/android/Tests/KeyboardHarness/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from android/Tests/Chirality/gradle/wrapper/gradle-wrapper.properties rename to android/Tests/KeyboardHarness/gradle/wrapper/gradle-wrapper.properties diff --git a/android/Tests/Chirality/gradlew b/android/Tests/KeyboardHarness/gradlew similarity index 100% rename from android/Tests/Chirality/gradlew rename to android/Tests/KeyboardHarness/gradlew diff --git a/android/Tests/Chirality/gradlew.bat b/android/Tests/KeyboardHarness/gradlew.bat similarity index 100% rename from android/Tests/Chirality/gradlew.bat rename to android/Tests/KeyboardHarness/gradlew.bat diff --git a/android/Tests/KeyboardHarness/readme.md b/android/Tests/KeyboardHarness/readme.md new file mode 100644 index 0000000000..aa04c0edc2 --- /dev/null +++ b/android/Tests/KeyboardHarness/readme.md @@ -0,0 +1,9 @@ +# KeyboardHarness Test App # + +This app is is based on Samples/KMSample1 and targets keyboard functionality and reproducing bugs with keyboards rather than integration functionality. Use as system keyboard is currently not supported. + +## Version History ## + +## 2017-09-26 1.0 +* Initial creation + diff --git a/android/Tests/Chirality/settings.gradle b/android/Tests/KeyboardHarness/settings.gradle similarity index 100% rename from android/Tests/Chirality/settings.gradle rename to android/Tests/KeyboardHarness/settings.gradle From f9f3dc7449c38028eb630aa11b366b7966e2948d Mon Sep 17 00:00:00 2001 From: darcywong00 Date: Wed, 27 Sep 2017 11:03:37 +0700 Subject: [PATCH 06/19] Document bitmasks and undo getActiveKeyboard refactor * Document shift states and lock states in kmembedded.js, and standardize `lstates` parameter name * Leave getActiveKeyboard() refactor as a TODO --- .../kmea/KMHardwareKeyboardInterpreter.java | 2 +- .../java/com/tavultesoft/kmea/KMManager.java | 44 ++++++++++++++----- .../app/src/main/res/values/strings.xml | 2 +- web/source/kmwembedded.js | 14 +++--- 4 files changed, 44 insertions(+), 18 deletions(-) diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java index ef9194c1b5..54021a3911 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java @@ -118,7 +118,7 @@ public class KMHardwareKeyboardInterpreter implements KeyEvent.Callback { return false; } - boolean isChiral = KMManager.getActiveKeyboard().getChirality(); + boolean isChiral = KMManager.getKeyboard(this.keyboardType).getChirality(); // States of modifier keys // KeyEvent.getModifiers() specifically masks out lock keys (KeyEvent.META_CAPS_LOCK_ON, diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java index d77cb74664..072ebbecd0 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java @@ -1160,7 +1160,12 @@ public final class KMManager { } if (result > 0) { - KMManager.getActiveKeyboard().loadKeyboard(); + if (KMManager.InAppKeyboard != null) { + InAppKeyboard.loadKeyboard(); + } + if ( KMManager.SystemKeyboard != null) { + SystemKeyboard.loadKeyboard(); + } } } }.execute(); @@ -1375,18 +1380,23 @@ public final class KMManager { String langName = kbInfo.get(KMManager.KMKey_LanguageName); String kFont = kbInfo.get(KMManager.KMKey_Font); String kOskFont = kbInfo.get(KMManager.KMKey_OskFont); - KMManager.getActiveKeyboard().setKeyboard(kbId, langId, kbName, langName, kFont, kOskFont); + if (InAppKeyboard != null) { + InAppKeyboard.setKeyboard(kbId, langId, kbName, langName, kFont, kOskFont); + } + if (SystemKeyboard != null) { + SystemKeyboard.setKeyboard(kbId, langId, kbName, langName, kFont, kOskFont); + } } - public static KMKeyboard getActiveKeyboard() { - Log.d("KMM", "InApp: " + String.valueOf(InAppKeyboard != null) + " SystemKeyboard: " + String.valueOf(SystemKeyboard != null)); - Log.d("KMM", "InAppKeyboardLoaded: " + String.valueOf(InAppKeyboardLoaded) + " SystemKeyboardLoaded: " + String.valueOf(SystemKeyboardLoaded)); - if (InAppKeyboard != null && InAppKeyboardLoaded) { + // TODO: Refactor InAppKeyboard / SystemKeyboard logic + public static KMKeyboard getKeyboard(KeyboardType keyboard) { + if (keyboard == KeyboardType.KEYBOARD_TYPE_INAPP) { return InAppKeyboard; - } else if (SystemKeyboard != null && SystemKeyboardLoaded) { + } else if (keyboard == KeyboardType.KEYBOARD_TYPE_SYSTEM) { return SystemKeyboard; } else { - Log.w("KMManager", "Cannot determine active keyboard"); + // What should we do if KeyboardType.KEYBOARD_TYPE_UNDEFINED? + Log.w("KMManager", "Invalid keyboard"); return null; } } @@ -1786,11 +1796,25 @@ public final class KMManager { } public static boolean isHelpBubbleEnabled() { - return KMManager.getActiveKeyboard().isHelpBubbleEnabled; + boolean retVal = true; + + if (InAppKeyboard != null) { + retVal = InAppKeyboard.isHelpBubbleEnabled; + } else if (SystemKeyboard != null) { + retVal = SystemKeyboard.isHelpBubbleEnabled; + } + + return retVal; } public static void setHelpBubbleEnabled(boolean newValue) { - KMManager.getActiveKeyboard().isHelpBubbleEnabled = newValue; + if (InAppKeyboard != null) { + InAppKeyboard.isHelpBubbleEnabled = newValue; + } + + if (SystemKeyboard != null) { + SystemKeyboard.isHelpBubbleEnabled = newValue; + } } public static boolean canAddNewKeyboard() { diff --git a/android/Tests/KeyboardHarness/app/src/main/res/values/strings.xml b/android/Tests/KeyboardHarness/app/src/main/res/values/strings.xml index c6c52903eb..0222c4a67c 100644 --- a/android/Tests/KeyboardHarness/app/src/main/res/values/strings.xml +++ b/android/Tests/KeyboardHarness/app/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ - KeyboardHarness + Keyboard Harness Hello world! Settings diff --git a/web/source/kmwembedded.js b/web/source/kmwembedded.js index 469c9b9a36..45c86031a4 100644 --- a/web/source/kmwembedded.js +++ b/web/source/kmwembedded.js @@ -455,10 +455,11 @@ * API endpoint for hardware keystroke events from Android external keyboards * * @param {number} code key identifier - * @param {number} shift shift state (0x10=shift 0x20=ctrl 0x40=alt) - * @param {number} lstate lock state (caps, num, scroll locks) + * @param {number} shift shift state (0x01=left ctrl 0x02=right ctrl 0x04=left alt 0x08=right alt + * 0x10=shift 0x20=ctrl 0x40=alt) + * @param {number} lstates lock state (0x0200=no caps 0x0400=num 0x0800=no num 0x1000=scroll 0x2000=no scroll locks) **/ - keymanweb['executeHardwareKeystroke'] = function(code, shift, lstate = 0) { + keymanweb['executeHardwareKeystroke'] = function(code, shift, lstates = 0) { if(!keymanweb._ActiveKeyboard || code == 0) { return false; } @@ -467,7 +468,7 @@ device.touchable = false; device.formFactor = 'desktop'; try { - result = keymanweb.executeHardwareKeystrokeInternal(code, shift, lstate); + result = keymanweb.executeHardwareKeystrokeInternal(code, shift, lstates); } catch (err) { console.error(err.message, err); } @@ -480,8 +481,9 @@ * Process the hardware key to the keyboard mapping * * @param {number} code key identifier - * @param {number} shift shift state (0x10=shift 0x20=ctrl 0x40=alt) - * @param {number} lstates lock state (caps, scroll, num locks) + * @param {number} shift shift state (0x01=left ctrl 0x02=right ctrl 0x04=left alt 0x08=right alt + * 0x10=shift 0x20=ctrl 0x40=alt) + * @param {number} lstates lock state (0x0200=no caps 0x0400=num 0x0800=no num 0x1000=scroll 0x2000=no scroll locks) **/ keymanweb.executeHardwareKeystrokeInternal = function(code, shift, lstates) { From 32bb4c69612d984e7b911ce1a9400242d2214d5f Mon Sep 17 00:00:00 2001 From: darcywong00 Date: Wed, 27 Sep 2017 16:14:20 +0700 Subject: [PATCH 07/19] Refactor JS call of setIsChiral() to onKeyboardChanged event Addresses PR comment of not using a timeout that assumes keyboard is ready in preference of KMW event (keyboard loaded or changed) This is still wip --- .../com/keyman/android/SystemKeyboard.java | 8 +++- .../com/tavultesoft/kmapro/MainActivity.java | 7 +++- .../KMEA/app/src/main/assets/keyboard.html | 1 + .../kmea/KMHardwareKeyboardInterpreter.java | 2 +- .../java/com/tavultesoft/kmea/KMKeyboard.java | 11 +++-- .../java/com/tavultesoft/kmea/KMManager.java | 42 +++++++++++++++++-- .../kmea/KeyboardEventHandler.java | 16 ++++--- 7 files changed, 68 insertions(+), 19 deletions(-) diff --git a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java index eec7a8a53a..a07c8d4ed9 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java +++ b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java @@ -14,6 +14,7 @@ import android.content.Context; import android.content.res.Configuration; import android.graphics.Point; import android.inputmethodservice.InputMethodService; +import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; @@ -159,15 +160,18 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven @Override public void onKeyboardLoaded(KeyboardType keyboardType) { + Log.d("SK", "onKeyboardLoaded " + String.valueOf(keyboardType)); if (keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) { if (exText != null) exText = null; } + KMManager.checkIsChiral(keyboardType); } @Override - public void onKeyboardChanged(String newKeyboard) { - // Do nothing + public void onKeyboardChanged(String newKeyboard, KeyboardType keyboardType) { + Log.d("SK", "onKeyboardChanged"); + KMManager.checkIsChiral(keyboardType); } @Override diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java index 0cfcaccdc2..0f5e31c529 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java @@ -258,16 +258,19 @@ public class MainActivity extends Activity implements OnKeyboardEventListener, O @Override public void onKeyboardLoaded(KeyboardType keyboardType) { - // Do nothing + KMManager.checkIsChiral(keyboardType); } @Override - public void onKeyboardChanged(String newKeyboard) { + public void onKeyboardChanged(String newKeyboard, KeyboardType keyboardType) { + Log.d("MainActivity", "onKeyboardChanged"); textView.setTypeface(KMManager.getKeyboardTextFontTypeface(this)); + KMManager.checkIsChiral(keyboardType); } @Override public void onKeyboardShown() { + Log.d("MainActivity", "onKeyboardShown"); resizeTextView(true); } diff --git a/android/KMEA/app/src/main/assets/keyboard.html b/android/KMEA/app/src/main/assets/keyboard.html index 4fca9b6c8c..f9ebef39d2 100644 --- a/android/KMEA/app/src/main/assets/keyboard.html +++ b/android/KMEA/app/src/main/assets/keyboard.html @@ -63,6 +63,7 @@ } else { isChiral = tavultesoft.keymanweb.isChiral(); } + window.console.log('isChiral is ' + isChiral); window.jsInterface.setIsChiral(isChiral); } diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java index 54021a3911..ecd56b90af 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java @@ -118,7 +118,7 @@ public class KMHardwareKeyboardInterpreter implements KeyEvent.Callback { return false; } - boolean isChiral = KMManager.getKeyboard(this.keyboardType).getChirality(); + boolean isChiral = KMManager.getChirality(); // States of modifier keys // KeyEvent.getModifiers() specifically masks out lock keys (KeyEvent.META_CAPS_LOCK_ON, diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMKeyboard.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMKeyboard.java index 2ba8ccf7e5..01a1ea8c34 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMKeyboard.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMKeyboard.java @@ -54,7 +54,7 @@ final class KMKeyboard extends WebView { private GestureDetector gestureDetector; private static ArrayList kbEventListeners = null; private boolean ShouldShowHelpBubble = false; - private boolean isChiral = false; +// private boolean isChiral = false; protected boolean keyboardSet = false; protected boolean keyboardPickerEnabled = true; @@ -298,6 +298,7 @@ final class KMKeyboard extends WebView { Log.d("KMKeyboard", jsString); } + /* // Add a little delay for the keyboard to load so the chirality can be properly detected Handler chiralityHandler = new Handler(); chiralityHandler.postDelayed(new Runnable() { @@ -306,7 +307,7 @@ final class KMKeyboard extends WebView { loadUrl("javascript:setIsChiral()"); } }, 1000); - +*/ currentKeyboard = kbKey; keyboardSet = true; saveCurrentKeyboardIndex(); @@ -398,6 +399,7 @@ final class KMKeyboard extends WebView { Log.d("KMKeyboard", jsString); } + /* // Add a little delay for the keyboard to load so the chirality can be properly detected Handler chiralityHandler = new Handler(); chiralityHandler.postDelayed(new Runnable() { @@ -406,7 +408,7 @@ final class KMKeyboard extends WebView { loadUrl("javascript:setIsChiral()"); } }, 1000); - +*/ currentKeyboard = kbKey; keyboardSet = true; saveCurrentKeyboardIndex(); @@ -425,6 +427,7 @@ final class KMKeyboard extends WebView { return retVal; } + /* public void setChirality(boolean flag) { this.isChiral = flag; } @@ -432,7 +435,7 @@ final class KMKeyboard extends WebView { public boolean getChirality() { return this.isChiral; } - + */ private void saveCurrentKeyboardIndex() { SharedPreferences prefs = context.getSharedPreferences(context.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java index 072ebbecd0..5e18b078e3 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java @@ -88,6 +88,7 @@ public final class KMManager { private static boolean shouldAllowSetKeyboard = true; private static ArrayList kbDownloadEventListeners = null; private static boolean didCopyAssets = false; + private static boolean isChiral = false; private static GlobeKeyAction inappKbGlobeKeyAction = GlobeKeyAction.GLOBE_KEY_ACTION_SHOW_MENU; private static GlobeKeyAction sysKbGlobeKeyAction = GlobeKeyAction.GLOBE_KEY_ACTION_SHOW_MENU; @@ -215,6 +216,20 @@ public final class KMManager { InAppKeyboard.setWebViewClient(new KMInAppKeyboardWebViewClient(appContext)); InAppKeyboard.addJavascriptInterface(new KMInAppKeyboardJSHandler(appContext), "jsInterface"); InAppKeyboard.loadKeyboard(); + + // TODO: Test code adding chirality keyboard that needs to be removed before merging + HashMap kbInfo = new HashMap(); + final String Chirality_KeyboardFont = "{\"family\":\"LatinWeb\",\"source\":[\"DejaVuSans.ttf\"]}"; + + kbInfo.put(KMManager.KMKey_KeyboardID, "chirality"); + kbInfo.put(KMManager.KMKey_LanguageID, "eng"); + kbInfo.put(KMManager.KMKey_KeyboardName, "Chirality Keyboard"); + kbInfo.put(KMManager.KMKey_LanguageName, "English"); + kbInfo.put(KMManager.KMKey_KeyboardVersion, "1.0"); + kbInfo.put(KMManager.KMKey_Font, Chirality_KeyboardFont); + addKeyboard(appContext, kbInfo); + Log.d("KMM", "initInApp"); + // end of test code } } @@ -274,6 +289,27 @@ public final class KMManager { return mainLayout; } + /* + public static void setIsChiral(KeyboardType keyboardType) { + Log.d("KMM", "kb: " + String.valueOf(keyboardType) + "InApp loaded: " + InAppKeyboardLoaded + " System loaded: " + SystemKeyboardLoaded); + KMManager.getKeyboard(keyboardType).loadUrl("javascript:setIsChiral()"); + } +*/ + public static void checkIsChiral(KeyboardType keyboardType) { + Log.d("KMM", "keyboardType: " + String.valueOf(keyboardType) + "In loaded: " + + String.valueOf(InAppKeyboardLoaded) + " Sys loaded: " + String.valueOf(SystemKeyboardLoaded)); + if (keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) { + SystemKeyboard.loadUrl("javascript:setIsChiral()"); + } + else { + InAppKeyboard.loadUrl("javascript:setIsChiral()"); + } + } + + public static boolean getChirality() { + return KMManager.isChiral; + } + public static void onStartInput(EditorInfo attribute, boolean restarting) { if (!restarting) { String packageName = attribute.packageName; @@ -2276,8 +2312,7 @@ public final class KMManager { } @JavascriptInterface - public void setIsChiral(boolean isChiral) { - InAppKeyboard.setChirality(isChiral); + public void setIsChiral(boolean isChiral) { KMManager.isChiral = isChiral; // { InAppKeyboard.setChirality(isChiral); } // This annotation is required in Jelly Bean and later: @@ -2386,8 +2421,7 @@ public final class KMManager { } @JavascriptInterface - public void setIsChiral(boolean isChiral) { - SystemKeyboard.setChirality(isChiral); + public void setIsChiral(boolean isChiral) { KMManager.isChiral = isChiral; // { SystemKeyboard.setChirality(isChiral); } // This annotation is required in Jelly Bean and later: diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KeyboardEventHandler.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KeyboardEventHandler.java index 1061e6220d..62f033af7e 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KeyboardEventHandler.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KeyboardEventHandler.java @@ -26,17 +26,21 @@ public final class KeyboardEventHandler { // make a copy of the list to avoid concurrent modification while iterating ArrayList _listeners = (ArrayList) listeners.clone(); if (event == EventType.KEYBOARD_LOADED) { - for (OnKeyboardEventListener listener : _listeners) + for (OnKeyboardEventListener listener : _listeners) { listener.onKeyboardLoaded(keyboardType); + } } else if (event == EventType.KEYBOARD_CHANGED) { - for (OnKeyboardEventListener listener : _listeners) - listener.onKeyboardChanged(newValue); + for (OnKeyboardEventListener listener : _listeners) { + listener.onKeyboardChanged(newValue, keyboardType); + } } else if (event == EventType.KEYBOARD_SHOWN) { - for (OnKeyboardEventListener listener : _listeners) + for (OnKeyboardEventListener listener : _listeners) { listener.onKeyboardShown(); + } } else if (event == EventType.KEYBOARD_DISMISSED) { - for (OnKeyboardEventListener listener : _listeners) + for (OnKeyboardEventListener listener : _listeners) { listener.onKeyboardDismissed(); + } } } } @@ -59,7 +63,7 @@ public final class KeyboardEventHandler { public static interface OnKeyboardEventListener { void onKeyboardLoaded(KeyboardType keyboardType); - void onKeyboardChanged(String newKeyboard); // newKeyboard string format: languageID_keyboardID e.g. eng_us + void onKeyboardChanged(String newKeyboard, KeyboardType keyboardType); // newKeyboard string format: languageID_keyboardID e.g. eng_us void onKeyboardShown(); From e2a8b763a1f6912774ca095b09af7d2f54292004 Mon Sep 17 00:00:00 2001 From: darcywong00 Date: Thu, 28 Sep 2017 16:01:30 +0700 Subject: [PATCH 08/19] Update isChiral on keyboard shown / changed - Update In-app keyboard `isChiral` property when the OSK is shown - Update System keyboard `isChiral` property on the event onKeyboardChanged() This implementation comes out of the complication when in-app or system keyboard is changed, KMManager.setKeyboard is changing both keyboards --- .../com/keyman/android/SystemKeyboard.java | 9 ++++--- .../com/tavultesoft/kmapro/MainActivity.java | 8 +++--- .../KMEA/app/src/main/assets/keyboard.html | 2 +- .../kmea/KMHardwareKeyboardInterpreter.java | 2 +- .../java/com/tavultesoft/kmea/KMKeyboard.java | 27 +++---------------- .../java/com/tavultesoft/kmea/KMManager.java | 26 +++++++++--------- .../java/com/tavultesoft/kmea/KMTextView.java | 6 +++++ 7 files changed, 33 insertions(+), 47 deletions(-) diff --git a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java index a07c8d4ed9..339b192bc2 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java +++ b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java @@ -40,6 +40,7 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven if (BuildConfig.DEBUG) { KMManager.setDebugMode(true); } + Log.d("SystemKeyboard", "addKeyboardEventListener"); KMManager.addKeyboardEventListener(this); KMManager.initialize(getApplicationContext(), KeyboardType.KEYBOARD_TYPE_SYSTEM); interpreter = new KMHardwareKeyboardInterpreter(getApplicationContext(), KeyboardType.KEYBOARD_TYPE_SYSTEM); @@ -160,18 +161,18 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven @Override public void onKeyboardLoaded(KeyboardType keyboardType) { - Log.d("SK", "onKeyboardLoaded " + String.valueOf(keyboardType)); if (keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) { if (exText != null) exText = null; } - KMManager.checkIsChiral(keyboardType); } @Override public void onKeyboardChanged(String newKeyboard, KeyboardType keyboardType) { - Log.d("SK", "onKeyboardChanged"); - KMManager.checkIsChiral(keyboardType); + // System keyboard changed, so refresh the chiral modifier status + if (keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) { + KMManager.checkIsChiral(KeyboardType.KEYBOARD_TYPE_SYSTEM); + } } @Override diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java index 0f5e31c529..2997f2efee 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java @@ -258,24 +258,22 @@ public class MainActivity extends Activity implements OnKeyboardEventListener, O @Override public void onKeyboardLoaded(KeyboardType keyboardType) { - KMManager.checkIsChiral(keyboardType); + // Do nothing } @Override public void onKeyboardChanged(String newKeyboard, KeyboardType keyboardType) { - Log.d("MainActivity", "onKeyboardChanged"); textView.setTypeface(KMManager.getKeyboardTextFontTypeface(this)); - KMManager.checkIsChiral(keyboardType); } @Override public void onKeyboardShown() { - Log.d("MainActivity", "onKeyboardShown"); resizeTextView(true); } @Override - public void onKeyboardDismissed() { + public void onKeyboardDismissed() + { resizeTextView(false); } diff --git a/android/KMEA/app/src/main/assets/keyboard.html b/android/KMEA/app/src/main/assets/keyboard.html index f9ebef39d2..14a1a526a6 100644 --- a/android/KMEA/app/src/main/assets/keyboard.html +++ b/android/KMEA/app/src/main/assets/keyboard.html @@ -63,7 +63,7 @@ } else { isChiral = tavultesoft.keymanweb.isChiral(); } - window.console.log('isChiral is ' + isChiral); + //window.console.log('isChiral is ' + isChiral); window.jsInterface.setIsChiral(isChiral); } diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java index ecd56b90af..54021a3911 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java @@ -118,7 +118,7 @@ public class KMHardwareKeyboardInterpreter implements KeyEvent.Callback { return false; } - boolean isChiral = KMManager.getChirality(); + boolean isChiral = KMManager.getKeyboard(this.keyboardType).getChirality(); // States of modifier keys // KeyEvent.getModifiers() specifically masks out lock keys (KeyEvent.META_CAPS_LOCK_ON, diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMKeyboard.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMKeyboard.java index 01a1ea8c34..74be9a794d 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMKeyboard.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMKeyboard.java @@ -54,7 +54,7 @@ final class KMKeyboard extends WebView { private GestureDetector gestureDetector; private static ArrayList kbEventListeners = null; private boolean ShouldShowHelpBubble = false; -// private boolean isChiral = false; + private boolean isChiral = false; protected boolean keyboardSet = false; protected boolean keyboardPickerEnabled = true; @@ -298,16 +298,6 @@ final class KMKeyboard extends WebView { Log.d("KMKeyboard", jsString); } - /* - // Add a little delay for the keyboard to load so the chirality can be properly detected - Handler chiralityHandler = new Handler(); - chiralityHandler.postDelayed(new Runnable() { - @Override - public void run() { - loadUrl("javascript:setIsChiral()"); - } - }, 1000); -*/ currentKeyboard = kbKey; keyboardSet = true; saveCurrentKeyboardIndex(); @@ -399,16 +389,6 @@ final class KMKeyboard extends WebView { Log.d("KMKeyboard", jsString); } - /* - // Add a little delay for the keyboard to load so the chirality can be properly detected - Handler chiralityHandler = new Handler(); - chiralityHandler.postDelayed(new Runnable() { - @Override - public void run() { - loadUrl("javascript:setIsChiral()"); - } - }, 1000); -*/ currentKeyboard = kbKey; keyboardSet = true; saveCurrentKeyboardIndex(); @@ -427,15 +407,16 @@ final class KMKeyboard extends WebView { return retVal; } - /* public void setChirality(boolean flag) { this.isChiral = flag; } public boolean getChirality() { + return this.isChiral; + } - */ + private void saveCurrentKeyboardIndex() { SharedPreferences prefs = context.getSharedPreferences(context.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java index 5e18b078e3..552bde48c4 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java @@ -88,7 +88,6 @@ public final class KMManager { private static boolean shouldAllowSetKeyboard = true; private static ArrayList kbDownloadEventListeners = null; private static boolean didCopyAssets = false; - private static boolean isChiral = false; private static GlobeKeyAction inappKbGlobeKeyAction = GlobeKeyAction.GLOBE_KEY_ACTION_SHOW_MENU; private static GlobeKeyAction sysKbGlobeKeyAction = GlobeKeyAction.GLOBE_KEY_ACTION_SHOW_MENU; @@ -228,7 +227,7 @@ public final class KMManager { kbInfo.put(KMManager.KMKey_KeyboardVersion, "1.0"); kbInfo.put(KMManager.KMKey_Font, Chirality_KeyboardFont); addKeyboard(appContext, kbInfo); - Log.d("KMM", "initInApp"); + Log.d("KMManager", "initInApp"); // end of test code } } @@ -289,15 +288,12 @@ public final class KMManager { return mainLayout; } - /* + public static void setIsChiral(KeyboardType keyboardType) { - Log.d("KMM", "kb: " + String.valueOf(keyboardType) + "InApp loaded: " + InAppKeyboardLoaded + " System loaded: " + SystemKeyboardLoaded); KMManager.getKeyboard(keyboardType).loadUrl("javascript:setIsChiral()"); } -*/ + public static void checkIsChiral(KeyboardType keyboardType) { - Log.d("KMM", "keyboardType: " + String.valueOf(keyboardType) + "In loaded: " + - String.valueOf(InAppKeyboardLoaded) + " Sys loaded: " + String.valueOf(SystemKeyboardLoaded)); if (keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) { SystemKeyboard.loadUrl("javascript:setIsChiral()"); } @@ -306,10 +302,6 @@ public final class KMManager { } } - public static boolean getChirality() { - return KMManager.isChiral; - } - public static void onStartInput(EditorInfo attribute, boolean restarting) { if (!restarting) { String packageName = attribute.packageName; @@ -2312,7 +2304,11 @@ public final class KMManager { } @JavascriptInterface - public void setIsChiral(boolean isChiral) { KMManager.isChiral = isChiral; // { InAppKeyboard.setChirality(isChiral); + public void setIsChiral(boolean isChiral) { + if (isDebugMode()) { + Log.d("KMManager", "InAppKeyboard chirality: " + String.valueOf(isChiral)); + } + InAppKeyboard.setChirality(isChiral); } // This annotation is required in Jelly Bean and later: @@ -2421,7 +2417,11 @@ public final class KMManager { } @JavascriptInterface - public void setIsChiral(boolean isChiral) { KMManager.isChiral = isChiral; // { SystemKeyboard.setChirality(isChiral); + public void setIsChiral(boolean isChiral) { + if (isDebugMode()) { + Log.d("KMManager", "SystemKeyboard chirality: " + String.valueOf(isChiral)); + } + SystemKeyboard.setChirality(isChiral); } // This annotation is required in Jelly Bean and later: diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMTextView.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMTextView.java index e4ab1c5149..96eb678b6f 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMTextView.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMTextView.java @@ -293,6 +293,12 @@ public final class KMTextView extends EditText { if (KMManager.isDebugMode()) Log.d("showKeyboard()", "Keyboard shown"); + + // In-app keyboard is shown, so refresh the chiral modifier status + if (KMManager.InAppKeyboard != null) { + KMManager.checkIsChiral(KeyboardType.KEYBOARD_TYPE_INAPP); + } + KeyboardEventHandler.notifyListeners(kbEventListeners, KeyboardType.KEYBOARD_TYPE_INAPP, EventType.KEYBOARD_SHOWN, null); } From d66f52b350e51737db10acefee7f3387fcf2e534 Mon Sep 17 00:00:00 2001 From: darcywong00 Date: Thu, 28 Sep 2017 16:58:31 +0700 Subject: [PATCH 09/19] Update Samples and Tests projects for handler API change Updates to new onKeyboardChanged signature Also incorporated external keyboard support to KMSample2 --- .../com/keyman/kmsample1/MainActivity.java | 2 +- .../com/keyman/kmsample2/SystemKeyboard.java | 49 ++++++++++++++----- .../tests/keyboardHarness/MainActivity.java | 5 +- 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/android/Samples/KMSample1/app/src/main/java/com/keyman/kmsample1/MainActivity.java b/android/Samples/KMSample1/app/src/main/java/com/keyman/kmsample1/MainActivity.java index c9dcf14dbe..3c27bed32b 100644 --- a/android/Samples/KMSample1/app/src/main/java/com/keyman/kmsample1/MainActivity.java +++ b/android/Samples/KMSample1/app/src/main/java/com/keyman/kmsample1/MainActivity.java @@ -92,7 +92,7 @@ public class MainActivity extends Activity implements OnKeyboardEventListener, O } @Override - public void onKeyboardChanged(String newKeyboard) { + public void onKeyboardChanged(String newKeyboard, KeyboardType keyboardType) { // Handle Keyman keyboard changed event here if needed } diff --git a/android/Samples/KMSample2/app/src/main/java/com/keyman/kmsample2/SystemKeyboard.java b/android/Samples/KMSample2/app/src/main/java/com/keyman/kmsample2/SystemKeyboard.java index 3faa6b0c6c..1c6e66a72b 100644 --- a/android/Samples/KMSample2/app/src/main/java/com/keyman/kmsample2/SystemKeyboard.java +++ b/android/Samples/KMSample2/app/src/main/java/com/keyman/kmsample2/SystemKeyboard.java @@ -1,9 +1,16 @@ package com.keyman.kmsample2; +import com.tavultesoft.kmea.KMManager; +import com.tavultesoft.kmea.KMManager.KeyboardType; +import com.tavultesoft.kmea.KMHardwareKeyboardInterpreter; +import com.tavultesoft.kmea.KeyboardEventHandler.OnKeyboardEventListener; + import android.content.Context; import android.content.res.Configuration; import android.graphics.Point; import android.inputmethodservice.InputMethodService; +import java.util.HashMap; +import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; @@ -12,15 +19,10 @@ import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; -import com.tavultesoft.kmea.KMManager; -import com.tavultesoft.kmea.KMManager.KeyboardType; -import com.tavultesoft.kmea.KeyboardEventHandler; - -import java.util.HashMap; - -public class SystemKeyboard extends InputMethodService implements KeyboardEventHandler.OnKeyboardEventListener { +public class SystemKeyboard extends InputMethodService implements OnKeyboardEventListener { private static View inputView = null; + private KMHardwareKeyboardInterpreter interpreter = null; /** * Main initialization of the input method component. Be sure to call @@ -32,6 +34,7 @@ public class SystemKeyboard extends InputMethodService implements KeyboardEventH KMManager.setDebugMode(true); KMManager.addKeyboardEventListener(this); KMManager.initialize(getApplicationContext(), KeyboardType.KEYBOARD_TYPE_SYSTEM); + interpreter = new KMHardwareKeyboardInterpreter(getApplicationContext(), KeyboardType.KEYBOARD_TYPE_SYSTEM); // Add a custom keyboard HashMap kbInfo = new HashMap(); @@ -48,6 +51,7 @@ public class SystemKeyboard extends InputMethodService implements KeyboardEventH public void onDestroy() { inputView = null; KMManager.removeKeyboardEventListener(this); + interpreter = null; // Throw it away, since we're losing our application's context. KMManager.onDestroy(); super.onDestroy(); } @@ -103,7 +107,7 @@ public class SystemKeyboard extends InputMethodService implements KeyboardEventH attribute.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI | EditorInfo.IME_FLAG_NO_FULLSCREEN; super.onStartInput(attribute, restarting); KMManager.onStartInput(attribute, restarting); - + KMManager.resetContext(KeyboardType.KEYBOARD_TYPE_SYSTEM); // User switched to a new input field so we should extract the text from input field // and pass it to Keyman Engine together with selection range InputConnection ic = getCurrentInputConnection(); @@ -158,11 +162,14 @@ public class SystemKeyboard extends InputMethodService implements KeyboardEventH // Handle Keyman keyboard loaded event here if needed // We can set our custom keyboard here int kbIndex = KMManager.getKeyboardIndex(this, "tamil99m", "tam"); - KMManager.setKeyboard(this, kbIndex); } @Override - public void onKeyboardChanged(String newKeyboard) { + public void onKeyboardChanged(String newKeyboard, KeyboardType keyboardType) { + // System keyboard changed, so refresh the chiral modifier status + if (keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) { + KMManager.checkIsChiral(KeyboardType.KEYBOARD_TYPE_SYSTEM); + } // Handle Keyman keyboard changed event here if needed } @@ -175,4 +182,24 @@ public class SystemKeyboard extends InputMethodService implements KeyboardEventH public void onKeyboardDismissed() { // Handle Keyman keyboard dismissed event here if needed } -} + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + return interpreter.onKeyDown(keyCode, event); // if false, will revert to default handling. + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + return interpreter.onKeyUp(keyCode, event); + } + + @Override + public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) { + return interpreter.onKeyMultiple(keyCode, count, event); + } + + @Override + public boolean onKeyLongPress(int keyCode, KeyEvent event) { + return interpreter.onKeyLongPress(keyCode, event); + } +} \ No newline at end of file diff --git a/android/Tests/KeyboardHarness/app/src/main/java/com/keyman/android/tests/keyboardHarness/MainActivity.java b/android/Tests/KeyboardHarness/app/src/main/java/com/keyman/android/tests/keyboardHarness/MainActivity.java index 040f04acb1..15e492f11b 100644 --- a/android/Tests/KeyboardHarness/app/src/main/java/com/keyman/android/tests/keyboardHarness/MainActivity.java +++ b/android/Tests/KeyboardHarness/app/src/main/java/com/keyman/android/tests/keyboardHarness/MainActivity.java @@ -88,13 +88,10 @@ public class MainActivity extends Activity implements OnKeyboardEventListener, O @Override public void onKeyboardLoaded(KeyboardType keyboardType) { // Handle Keyman keyboard loaded event here if needed - // We can set our custom keyboard here - int kbIndex = KMManager.getKeyboardIndex(this, "chirality", "eng"); - KMManager.setKeyboard(this, kbIndex); } @Override - public void onKeyboardChanged(String newKeyboard) { + public void onKeyboardChanged(String newKeyboard, KeyboardType keyboardType) { // Handle Keyman keyboard changed event here if needed } From c8e54b9767e558f1dbd4f3f98a92936600381b60 Mon Sep 17 00:00:00 2001 From: darcywong00 Date: Thu, 28 Sep 2017 22:39:42 +0700 Subject: [PATCH 10/19] Remove test chirality keyboard (again) from kmapro --- .../main/java/com/tavultesoft/kmea/KMManager.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java index 552bde48c4..711c1a9a0c 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java @@ -215,20 +215,6 @@ public final class KMManager { InAppKeyboard.setWebViewClient(new KMInAppKeyboardWebViewClient(appContext)); InAppKeyboard.addJavascriptInterface(new KMInAppKeyboardJSHandler(appContext), "jsInterface"); InAppKeyboard.loadKeyboard(); - - // TODO: Test code adding chirality keyboard that needs to be removed before merging - HashMap kbInfo = new HashMap(); - final String Chirality_KeyboardFont = "{\"family\":\"LatinWeb\",\"source\":[\"DejaVuSans.ttf\"]}"; - - kbInfo.put(KMManager.KMKey_KeyboardID, "chirality"); - kbInfo.put(KMManager.KMKey_LanguageID, "eng"); - kbInfo.put(KMManager.KMKey_KeyboardName, "Chirality Keyboard"); - kbInfo.put(KMManager.KMKey_LanguageName, "English"); - kbInfo.put(KMManager.KMKey_KeyboardVersion, "1.0"); - kbInfo.put(KMManager.KMKey_Font, Chirality_KeyboardFont); - addKeyboard(appContext, kbInfo); - Log.d("KMManager", "initInApp"); - // end of test code } } From e5074621463c862017d291515130dac892a6ad9d Mon Sep 17 00:00:00 2001 From: darcywong00 Date: Mon, 2 Oct 2017 10:45:10 +0700 Subject: [PATCH 11/19] Move chirality checks to KMEA Attempt to move to JS handler in KMManager --- .../com/keyman/android/SystemKeyboard.java | 5 +- .../KMEA/app/src/main/assets/keyboard.html | 10 ++- .../src/main/assets/languages/chirality.js | 79 +++++++++++++++++++ .../kmea/KMHardwareKeyboardInterpreter.java | 2 +- .../java/com/tavultesoft/kmea/KMManager.java | 55 +++++++++++-- .../java/com/tavultesoft/kmea/KMTextView.java | 5 -- 6 files changed, 138 insertions(+), 18 deletions(-) create mode 100644 android/KMEA/app/src/main/assets/languages/chirality.js diff --git a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java index 339b192bc2..24733e34a9 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java +++ b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java @@ -169,10 +169,7 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven @Override public void onKeyboardChanged(String newKeyboard, KeyboardType keyboardType) { - // System keyboard changed, so refresh the chiral modifier status - if (keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) { - KMManager.checkIsChiral(KeyboardType.KEYBOARD_TYPE_SYSTEM); - } + // Do nothing } @Override diff --git a/android/KMEA/app/src/main/assets/keyboard.html b/android/KMEA/app/src/main/assets/keyboard.html index 14a1a526a6..89af1a5197 100644 --- a/android/KMEA/app/src/main/assets/keyboard.html +++ b/android/KMEA/app/src/main/assets/keyboard.html @@ -63,8 +63,16 @@ } else { isChiral = tavultesoft.keymanweb.isChiral(); } + window.console.log("keyboard.html setIsChiral " + isChiral); + //window.console.log('isChiral is ' + isChiral); - window.jsInterface.setIsChiral(isChiral); + //window.jsInterface.setIsChiral(isChiral); + + fragmentToggle = (fragmentToggle + 1) % 100; + var div = document.createElement('div'); + div.innerHTML = t; + var dt = div.firstChild.nodeValue; + window.location.hash = 'setIsChiral-'+fragmentToggle+'+chiral='+isChiral; } function setKeymanLanguage(keyboardName, internalName, languageName, langId, version, font, oskFont) { diff --git a/android/KMEA/app/src/main/assets/languages/chirality.js b/android/KMEA/app/src/main/assets/languages/chirality.js new file mode 100644 index 0000000000..ade1c50d02 --- /dev/null +++ b/android/KMEA/app/src/main/assets/languages/chirality.js @@ -0,0 +1,79 @@ +KeymanWeb.KR(new Keyboard_chirality()); + +/** + * Please note that this is an experimental handwritten keyboard designed for initial testing of modifier chirality support for KeymanWeb. + * As it is handcoded, it uses multiple codesize optimization techniques that Keyman Developer simply does not and will not employ, and it + * will only work with KeymanWeb based implementations. (Not supported for Keyman Desktop or Keyman for Mac.) + * + * (This keyboard was written before Developer's implementation of chirality support and was used to bootstrap KeymanWeb chirality, serving + * as the initial wave of testing.) + */ + +function Keyboard_chirality() { + this.KI = "Keyboard_chirality"; + this.KN = "Development Chirality Test Keyboard"; + this.KV = { + KMBM: 0x001F, + F: ' 1em "Arial"', + K102: 0, + KLS: { 'default': new Array("`", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "", "", "", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\\", "", "", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "", "", "", "", "", "", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "", "", "", "", "", ""), + 'shift': new Array("~", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "", "", "", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{", "}", "|", "", "", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "", "", "", "", "", "", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "", "", "", "", "", ""), + 'leftctrl': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ʍ", "ə", "ɹ", "ð", "ʏ", "ʉ", "ɨ", "ɵ", "ʘ", "", "", "", "", "", "", "ɑ", "ʃ", "", "ɸ", "ɣ", "ɥ", "ɟ", "", "ɬ", "ː", "", "", "", "", "", "", "", "ʒ", "χ", "ç", "ʋ", "β", "ɲ", "", "", "", "", "", "", "", "", "", ""), + 'leftctrl-shift': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ʡ", "", "ɘ", "ʀ", "θ", "", "ɯ", "ɪ", "", "", "", "", "", "", "", "", "ɒ", "ᶘ", "", "", "ɢ", "ʜ", "", "", "ʟ", "ː", "", "", "", "", "", "", "", "ᶚ", "", "", "", "ʙ", "ɴ", "", "", "", "ʔ", "", "", "", "", "", ""), + 'leftalt': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ɛ", "ɽ", "ʈ", "ɥ", "ʊ", "", "ɔ", "", "", "", "", "", "", "", "æ", "", "ɖ", "", "", "ɦ", "ʝ", "", "ɭ", "", "", "", "", "", "", "", "", "ʐ", "", "ɕ", "", "", "ɳ", "", "", "", "", "", "", "", "", "", ""), + 'rightalt': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ʠ", "ɰ", "ɜ", "ɾ", "ƭ", "", "ʌ", "", "ø", "ƥ", "", "", "", "", "", "", "ɐ", "σ", "ɗ", "", "ɠ", "ħ", "ʄ", "ƙ", "ɮ", "", "", "", "", "", "", "", "", "ʑ", "", "ƈ", "", "ɓ", "ŋ", "ɱ", "", "", "", "", "", "", "", "", ""), + 'leftalt-shift': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ʢ", "", "œ", "ɻ", "", "", "", "ᵻ", "ɞ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ʎ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""), + 'rightalt-shift': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ɶ", "ʁ", "", "", "ᵾ", "ᵼ", "ɤ", "", "", "", "", "", "", "", "ᴂ", "", "", "", "ʛ", "ɧ", "", "", "ɺ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "") + } + }; + this.KH = ''; + this.KM = 0; + + this.dfltCodes = ["K_BKQUOTE","K_1","K_2","K_3","K_4","K_5","K_6","K_7","K_8","K_9","K_0", + "K_HYPHEN","K_EQUAL","K_*","K_*","K_*","K_Q","K_W","K_E","K_R","K_T", + "K_Y","K_U","K_I","K_O","K_P","K_LBRKT","K_RBRKT","K_BKSLASH","K_*", + "K_*","K_*","K_A","K_S","K_D","K_F","K_G","K_H","K_J","K_K","K_L", + "K_COLON","K_QUOTE","K_*","K_*","K_*","K_*","K_*","K_oE2", + "K_Z","K_X","K_C","K_V","K_B","K_N","K_M","K_COMMA","K_PERIOD", + "K_SLASH","K_*","K_*","K_*","K_*","K_*","K_SPACE"]; + + this.gs = function (t, e) { + return this.g0(t, e); + }; + this.g0 = function (t, e) { + var k = KeymanWeb, r = 0, m = 0; + + // Handwritten time! + var kls = this.KV.KLS; + + var layers = ['default', 'shift', 'leftctrl', 'leftctrl-shift', 'leftalt', 'rightalt', 'leftalt-shift', 'rightalt-shift']; + + // Maps keystrokes by base key-codes and array into the key symbols displayed in KLS. + for(var i = 0; i < layers.length; i++) { + // Obtain the modifier code to match for the selected layer. + // The following uses a non-public property potentially subject to change in the future. + var modCode = k.osk.modifierCodes['VIRTUAL_KEY'] | k.osk.getModifierState(layers[i]); + var layer = layers[i]; + + for(var key=0; key < kls[layer].length; key++) { + var keySymbol = this.dfltCodes[key]; + + if(keySymbol == "K_*") { + continue; + } else if(kls[layer][key] != '') { + if (k.KKM(e, modCode, k.osk.keyCodes[keySymbol])) { + r = m = 1; + if(k.KSM(e, k.osk.modifierCodes['CAPS'])) { + k.KO(0, t, kls[layer][key].toUpperCase()); + } else { + k.KO(0, t, kls[layer][key]); + } + return r; + } + } + } + } + + return r; + }; +} \ No newline at end of file diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java index 54021a3911..c0de2d7004 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java @@ -118,7 +118,7 @@ public class KMHardwareKeyboardInterpreter implements KeyEvent.Callback { return false; } - boolean isChiral = KMManager.getKeyboard(this.keyboardType).getChirality(); + boolean isChiral = KMManager.getKMKeyboard(this.keyboardType).getChirality(); // States of modifier keys // KeyEvent.getModifiers() specifically masks out lock keys (KeyEvent.META_CAPS_LOCK_ON, diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java index 711c1a9a0c..2199b17b06 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java @@ -215,6 +215,20 @@ public final class KMManager { InAppKeyboard.setWebViewClient(new KMInAppKeyboardWebViewClient(appContext)); InAppKeyboard.addJavascriptInterface(new KMInAppKeyboardJSHandler(appContext), "jsInterface"); InAppKeyboard.loadKeyboard(); + + // TODO: Test code adding chirality keyboard that needs to be removed before merging + HashMap kbInfo = new HashMap(); + final String Chirality_KeyboardFont = "{\"family\":\"LatinWeb\",\"source\":[\"DejaVuSans.ttf\"]}"; + + kbInfo.put(KMManager.KMKey_KeyboardID, "chirality"); + kbInfo.put(KMManager.KMKey_LanguageID, "eng"); + kbInfo.put(KMManager.KMKey_KeyboardName, "Chirality Keyboard"); + kbInfo.put(KMManager.KMKey_LanguageName, "English"); + kbInfo.put(KMManager.KMKey_KeyboardVersion, "1.0"); + kbInfo.put(KMManager.KMKey_Font, Chirality_KeyboardFont); + addKeyboard(appContext, kbInfo); + Log.d("KMManager", "initInApp"); + // end of test code } } @@ -274,11 +288,6 @@ public final class KMManager { return mainLayout; } - - public static void setIsChiral(KeyboardType keyboardType) { - KMManager.getKeyboard(keyboardType).loadUrl("javascript:setIsChiral()"); - } - public static void checkIsChiral(KeyboardType keyboardType) { if (keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) { SystemKeyboard.loadUrl("javascript:setIsChiral()"); @@ -1403,7 +1412,7 @@ public final class KMManager { } // TODO: Refactor InAppKeyboard / SystemKeyboard logic - public static KMKeyboard getKeyboard(KeyboardType keyboard) { + public static KMKeyboard getKMKeyboard(KeyboardType keyboard) { if (keyboard == KeyboardType.KEYBOARD_TYPE_INAPP) { return InAppKeyboard; } else if (keyboard == KeyboardType.KEYBOARD_TYPE_SYSTEM) { @@ -2063,6 +2072,16 @@ public final class KMManager { hashMap.put("keyText", keyText); InAppKeyboard.subKeysList.add(hashMap); } + } else if (url.indexOf("setIsChiral") >= 0) { + int start = url.indexOf("chiral=") + 7; + String t = url.substring(start); + String chirality = url.substring(start); + Log.d("shouldOverride", "chirality is " + chirality); + if (InAppKeyboard.getVisibility() == View.VISIBLE) { + InAppKeyboard.setChirality(chirality == "true"); + } else if (SystemKeyboard.getVisibility() == View.VISIBLE) { + SystemKeyboard.setChirality(chirality == "true"); + } } return false; @@ -2260,13 +2279,35 @@ public final class KMManager { } } - private static final class KMInAppKeyboardJSHandler { + private static final class KMInAppKeyboardJSHandler implements OnKeyboardEventListener { private Context context; KMInAppKeyboardJSHandler(Context context) { this.context = context; } + @Override + public void onKeyboardLoaded(KeyboardType keyboardType){ + Log.d("KMInJSHandler", "checkIsChiral"); + InAppKeyboard.loadUrl("javascript:setIsChiral()"); + } + + @Override + public void onKeyboardChanged(String newKeyboard, KeyboardType keyboardType){ + // newKeyboard string format: languageID_keyboardID e.g. eng_us + InAppKeyboard.loadUrl("javascript:setIsChiral()"); + } + + @Override + public void onKeyboardShown() { + // Do nothing + } + + @Override + public void onKeyboardDismissed(){ + // Do nothing + } + // This annotation is required in Jelly Bean and later: @JavascriptInterface public String getDeviceType() { diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMTextView.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMTextView.java index 96eb678b6f..7d2e5e2cc4 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMTextView.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMTextView.java @@ -294,11 +294,6 @@ public final class KMTextView extends EditText { if (KMManager.isDebugMode()) Log.d("showKeyboard()", "Keyboard shown"); - // In-app keyboard is shown, so refresh the chiral modifier status - if (KMManager.InAppKeyboard != null) { - KMManager.checkIsChiral(KeyboardType.KEYBOARD_TYPE_INAPP); - } - KeyboardEventHandler.notifyListeners(kbEventListeners, KeyboardType.KEYBOARD_TYPE_INAPP, EventType.KEYBOARD_SHOWN, null); } From aac2368405d281dbac971dc722971cb49f89b143 Mon Sep 17 00:00:00 2001 From: darcywong00 Date: Mon, 2 Oct 2017 16:17:37 +0700 Subject: [PATCH 12/19] Cleanup after attaching to KMW event Cleanup code after moving chirality test to KMW event handler --- .../com/keyman/android/SystemKeyboard.java | 9 ++-- .../com/tavultesoft/kmapro/MainActivity.java | 5 +-- .../KMEA/app/src/main/assets/keyboard.html | 22 ++++----- .../java/com/tavultesoft/kmea/KMManager.java | 45 +------------------ .../java/com/tavultesoft/kmea/KMTextView.java | 1 - .../kmea/KeyboardEventHandler.java | 16 +++---- 6 files changed, 26 insertions(+), 72 deletions(-) diff --git a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java index 24733e34a9..e3ceffa0c0 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java +++ b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java @@ -8,13 +8,12 @@ import com.tavultesoft.kmapro.BuildConfig; import com.tavultesoft.kmea.KMManager; import com.tavultesoft.kmea.KMManager.KeyboardType; import com.tavultesoft.kmea.KMHardwareKeyboardInterpreter; -import com.tavultesoft.kmea.KeyboardEventHandler.OnKeyboardEventListener; +import com.tavultesoft.kmea.KeyboardEventHandler; import android.content.Context; import android.content.res.Configuration; import android.graphics.Point; import android.inputmethodservice.InputMethodService; -import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; @@ -24,7 +23,8 @@ import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; -public class SystemKeyboard extends InputMethodService implements OnKeyboardEventListener { + +public class SystemKeyboard extends InputMethodService implements KeyboardEventHandler.OnKeyboardEventListener { private static View inputView = null; private static ExtractedText exText = null; @@ -40,7 +40,6 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven if (BuildConfig.DEBUG) { KMManager.setDebugMode(true); } - Log.d("SystemKeyboard", "addKeyboardEventListener"); KMManager.addKeyboardEventListener(this); KMManager.initialize(getApplicationContext(), KeyboardType.KEYBOARD_TYPE_SYSTEM); interpreter = new KMHardwareKeyboardInterpreter(getApplicationContext(), KeyboardType.KEYBOARD_TYPE_SYSTEM); @@ -168,7 +167,7 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven } @Override - public void onKeyboardChanged(String newKeyboard, KeyboardType keyboardType) { + public void onKeyboardChanged(String newKeyboard) { // Do nothing } diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java index 2997f2efee..0cfcaccdc2 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java @@ -262,7 +262,7 @@ public class MainActivity extends Activity implements OnKeyboardEventListener, O } @Override - public void onKeyboardChanged(String newKeyboard, KeyboardType keyboardType) { + public void onKeyboardChanged(String newKeyboard) { textView.setTypeface(KMManager.getKeyboardTextFontTypeface(this)); } @@ -272,8 +272,7 @@ public class MainActivity extends Activity implements OnKeyboardEventListener, O } @Override - public void onKeyboardDismissed() - { + public void onKeyboardDismissed() { resizeTextView(false); } diff --git a/android/KMEA/app/src/main/assets/keyboard.html b/android/KMEA/app/src/main/assets/keyboard.html index 89af1a5197..899c7a1de1 100644 --- a/android/KMEA/app/src/main/assets/keyboard.html +++ b/android/KMEA/app/src/main/assets/keyboard.html @@ -27,6 +27,16 @@ kmw['getOskHeight'] = getOskHeight; kmw['getOskWidth'] = getOskWidth; kmw['setActiveElement']('ta'); + + kmw.addEventListener('keyboardchange', function(internalName, languageCode) { + setIsChiral(); + return true; + }); + kmw.addEventListener('keyboardloaded', function(internalName) { + setIsChiral(); + return true; + }); + ta.readOnly = false; } @@ -63,16 +73,8 @@ } else { isChiral = tavultesoft.keymanweb.isChiral(); } - window.console.log("keyboard.html setIsChiral " + isChiral); - - //window.console.log('isChiral is ' + isChiral); - //window.jsInterface.setIsChiral(isChiral); - - fragmentToggle = (fragmentToggle + 1) % 100; - var div = document.createElement('div'); - div.innerHTML = t; - var dt = div.firstChild.nodeValue; - window.location.hash = 'setIsChiral-'+fragmentToggle+'+chiral='+isChiral; + window.console.log("keyboard.html: setIsChiral " + isChiral); + window.jsInterface.setIsChiral(isChiral); } function setKeymanLanguage(keyboardName, internalName, languageName, langId, version, font, oskFont) { diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java index 2199b17b06..44bbb02267 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java @@ -288,15 +288,6 @@ public final class KMManager { return mainLayout; } - public static void checkIsChiral(KeyboardType keyboardType) { - if (keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) { - SystemKeyboard.loadUrl("javascript:setIsChiral()"); - } - else { - InAppKeyboard.loadUrl("javascript:setIsChiral()"); - } - } - public static void onStartInput(EditorInfo attribute, boolean restarting) { if (!restarting) { String packageName = attribute.packageName; @@ -2072,18 +2063,7 @@ public final class KMManager { hashMap.put("keyText", keyText); InAppKeyboard.subKeysList.add(hashMap); } - } else if (url.indexOf("setIsChiral") >= 0) { - int start = url.indexOf("chiral=") + 7; - String t = url.substring(start); - String chirality = url.substring(start); - Log.d("shouldOverride", "chirality is " + chirality); - if (InAppKeyboard.getVisibility() == View.VISIBLE) { - InAppKeyboard.setChirality(chirality == "true"); - } else if (SystemKeyboard.getVisibility() == View.VISIBLE) { - SystemKeyboard.setChirality(chirality == "true"); - } } - return false; } } @@ -2279,35 +2259,13 @@ public final class KMManager { } } - private static final class KMInAppKeyboardJSHandler implements OnKeyboardEventListener { + private static final class KMInAppKeyboardJSHandler { private Context context; KMInAppKeyboardJSHandler(Context context) { this.context = context; } - @Override - public void onKeyboardLoaded(KeyboardType keyboardType){ - Log.d("KMInJSHandler", "checkIsChiral"); - InAppKeyboard.loadUrl("javascript:setIsChiral()"); - } - - @Override - public void onKeyboardChanged(String newKeyboard, KeyboardType keyboardType){ - // newKeyboard string format: languageID_keyboardID e.g. eng_us - InAppKeyboard.loadUrl("javascript:setIsChiral()"); - } - - @Override - public void onKeyboardShown() { - // Do nothing - } - - @Override - public void onKeyboardDismissed(){ - // Do nothing - } - // This annotation is required in Jelly Bean and later: @JavascriptInterface public String getDeviceType() { @@ -2330,6 +2288,7 @@ public final class KMManager { return kbWidth; } + // Store the current keyboard chirality status from KMW in InAppKeyboard @JavascriptInterface public void setIsChiral(boolean isChiral) { if (isDebugMode()) { diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMTextView.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMTextView.java index 7d2e5e2cc4..e4ab1c5149 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMTextView.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMTextView.java @@ -293,7 +293,6 @@ public final class KMTextView extends EditText { if (KMManager.isDebugMode()) Log.d("showKeyboard()", "Keyboard shown"); - KeyboardEventHandler.notifyListeners(kbEventListeners, KeyboardType.KEYBOARD_TYPE_INAPP, EventType.KEYBOARD_SHOWN, null); } diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KeyboardEventHandler.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KeyboardEventHandler.java index 62f033af7e..1061e6220d 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KeyboardEventHandler.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KeyboardEventHandler.java @@ -26,21 +26,17 @@ public final class KeyboardEventHandler { // make a copy of the list to avoid concurrent modification while iterating ArrayList _listeners = (ArrayList) listeners.clone(); if (event == EventType.KEYBOARD_LOADED) { - for (OnKeyboardEventListener listener : _listeners) { + for (OnKeyboardEventListener listener : _listeners) listener.onKeyboardLoaded(keyboardType); - } } else if (event == EventType.KEYBOARD_CHANGED) { - for (OnKeyboardEventListener listener : _listeners) { - listener.onKeyboardChanged(newValue, keyboardType); - } + for (OnKeyboardEventListener listener : _listeners) + listener.onKeyboardChanged(newValue); } else if (event == EventType.KEYBOARD_SHOWN) { - for (OnKeyboardEventListener listener : _listeners) { + for (OnKeyboardEventListener listener : _listeners) listener.onKeyboardShown(); - } } else if (event == EventType.KEYBOARD_DISMISSED) { - for (OnKeyboardEventListener listener : _listeners) { + for (OnKeyboardEventListener listener : _listeners) listener.onKeyboardDismissed(); - } } } } @@ -63,7 +59,7 @@ public final class KeyboardEventHandler { public static interface OnKeyboardEventListener { void onKeyboardLoaded(KeyboardType keyboardType); - void onKeyboardChanged(String newKeyboard, KeyboardType keyboardType); // newKeyboard string format: languageID_keyboardID e.g. eng_us + void onKeyboardChanged(String newKeyboard); // newKeyboard string format: languageID_keyboardID e.g. eng_us void onKeyboardShown(); From bc208fba5dd06900b70a46d224b3ec7cd9102162 Mon Sep 17 00:00:00 2001 From: darcywong00 Date: Mon, 2 Oct 2017 16:30:59 +0700 Subject: [PATCH 13/19] Cleanup sample projects Revert to original onKeyboardChanged() signature --- .../src/main/java/com/keyman/android/SystemKeyboard.java | 4 ++-- .../app/src/main/java/com/tavultesoft/kmea/KMManager.java | 1 + .../app/src/main/java/com/keyman/kmsample1/MainActivity.java | 2 +- .../src/main/java/com/keyman/kmsample2/SystemKeyboard.java | 4 ---- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java index e3ceffa0c0..c8e65072cf 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java +++ b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java @@ -8,7 +8,7 @@ import com.tavultesoft.kmapro.BuildConfig; import com.tavultesoft.kmea.KMManager; import com.tavultesoft.kmea.KMManager.KeyboardType; import com.tavultesoft.kmea.KMHardwareKeyboardInterpreter; -import com.tavultesoft.kmea.KeyboardEventHandler; +import com.tavultesoft.kmea.KeyboardEventHandler.OnKeyboardEventListener; import android.content.Context; import android.content.res.Configuration; @@ -24,7 +24,7 @@ import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; -public class SystemKeyboard extends InputMethodService implements KeyboardEventHandler.OnKeyboardEventListener { +public class SystemKeyboard extends InputMethodService implements OnKeyboardEventListener { private static View inputView = null; private static ExtractedText exText = null; diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java index 44bbb02267..577774a219 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java @@ -2402,6 +2402,7 @@ public final class KMManager { return kbWidth; } + // Store the current keyboard chirality status from KMW in SystemKeyboard @JavascriptInterface public void setIsChiral(boolean isChiral) { if (isDebugMode()) { diff --git a/android/Samples/KMSample1/app/src/main/java/com/keyman/kmsample1/MainActivity.java b/android/Samples/KMSample1/app/src/main/java/com/keyman/kmsample1/MainActivity.java index 3c27bed32b..c9dcf14dbe 100644 --- a/android/Samples/KMSample1/app/src/main/java/com/keyman/kmsample1/MainActivity.java +++ b/android/Samples/KMSample1/app/src/main/java/com/keyman/kmsample1/MainActivity.java @@ -92,7 +92,7 @@ public class MainActivity extends Activity implements OnKeyboardEventListener, O } @Override - public void onKeyboardChanged(String newKeyboard, KeyboardType keyboardType) { + public void onKeyboardChanged(String newKeyboard) { // Handle Keyman keyboard changed event here if needed } diff --git a/android/Samples/KMSample2/app/src/main/java/com/keyman/kmsample2/SystemKeyboard.java b/android/Samples/KMSample2/app/src/main/java/com/keyman/kmsample2/SystemKeyboard.java index 1c6e66a72b..44d816364f 100644 --- a/android/Samples/KMSample2/app/src/main/java/com/keyman/kmsample2/SystemKeyboard.java +++ b/android/Samples/KMSample2/app/src/main/java/com/keyman/kmsample2/SystemKeyboard.java @@ -166,10 +166,6 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven @Override public void onKeyboardChanged(String newKeyboard, KeyboardType keyboardType) { - // System keyboard changed, so refresh the chiral modifier status - if (keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) { - KMManager.checkIsChiral(KeyboardType.KEYBOARD_TYPE_SYSTEM); - } // Handle Keyman keyboard changed event here if needed } From 3a30d2368418f9a38862ffa5c8a4b3a15ecc1309 Mon Sep 17 00:00:00 2001 From: darcywong00 Date: Tue, 3 Oct 2017 07:50:36 +0700 Subject: [PATCH 14/19] Clean up TestHarness Fix onKeyboardChanged signature --- .../kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java | 1 - .../com/keyman/android/tests/keyboardHarness/MainActivity.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java index c8e65072cf..eec7a8a53a 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java +++ b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java @@ -23,7 +23,6 @@ import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; - public class SystemKeyboard extends InputMethodService implements OnKeyboardEventListener { private static View inputView = null; diff --git a/android/Tests/KeyboardHarness/app/src/main/java/com/keyman/android/tests/keyboardHarness/MainActivity.java b/android/Tests/KeyboardHarness/app/src/main/java/com/keyman/android/tests/keyboardHarness/MainActivity.java index 15e492f11b..fe6d5025b3 100644 --- a/android/Tests/KeyboardHarness/app/src/main/java/com/keyman/android/tests/keyboardHarness/MainActivity.java +++ b/android/Tests/KeyboardHarness/app/src/main/java/com/keyman/android/tests/keyboardHarness/MainActivity.java @@ -91,7 +91,7 @@ public class MainActivity extends Activity implements OnKeyboardEventListener, O } @Override - public void onKeyboardChanged(String newKeyboard, KeyboardType keyboardType) { + public void onKeyboardChanged(String newKeyboard) { // Handle Keyman keyboard changed event here if needed } From adee729b8a92170c37df1ca2b35755f31f590ffd Mon Sep 17 00:00:00 2001 From: darcywong00 Date: Tue, 3 Oct 2017 08:36:56 +0700 Subject: [PATCH 15/19] Clarify setIsChiral parameter --- android/KMEA/app/src/main/assets/keyboard.html | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/android/KMEA/app/src/main/assets/keyboard.html b/android/KMEA/app/src/main/assets/keyboard.html index 899c7a1de1..1c298b2f25 100644 --- a/android/KMEA/app/src/main/assets/keyboard.html +++ b/android/KMEA/app/src/main/assets/keyboard.html @@ -66,14 +66,16 @@ return oskWidth; } - function setIsChiral(m) { + // Query KMW if keyboardID is a keyboard with chiral modifiers. + // If keyboardID not given, use active keyboard + function setIsChiral(keyboardID) { var isChiral = false; - if (typeof m !== "undefined") { - isChiral = tavultesoft.keymanweb.isChiral(m); + if (typeof keyboardID !== "undefined") { + isChiral = tavultesoft.keymanweb.isChiral(keyboardID); } else { isChiral = tavultesoft.keymanweb.isChiral(); } - window.console.log("keyboard.html: setIsChiral " + isChiral); + window.console.log('Keyboard setIsChiral = ' + isChiral); window.jsInterface.setIsChiral(isChiral); } From ced4102bedc65452832b0c798c158a9403eb3c62 Mon Sep 17 00:00:00 2001 From: darcywong00 Date: Tue, 3 Oct 2017 14:16:49 +0700 Subject: [PATCH 16/19] Update calls to kmw.addEventLIstener --- .../KMEA/app/src/main/assets/keyboard.html | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/android/KMEA/app/src/main/assets/keyboard.html b/android/KMEA/app/src/main/assets/keyboard.html index 1c298b2f25..83f9ac8250 100644 --- a/android/KMEA/app/src/main/assets/keyboard.html +++ b/android/KMEA/app/src/main/assets/keyboard.html @@ -27,17 +27,10 @@ kmw['getOskHeight'] = getOskHeight; kmw['getOskWidth'] = getOskWidth; kmw['setActiveElement']('ta'); - - kmw.addEventListener('keyboardchange', function(internalName, languageCode) { - setIsChiral(); - return true; - }); - kmw.addEventListener('keyboardloaded', function(internalName) { - setIsChiral(); - return true; - }); - ta.readOnly = false; + + kmw.addEventListener('keyboardloaded', setIsChiral); + kmw.addEventListener('keyboardchange', setIsChiral); } function setOskHeight(h) { @@ -66,17 +59,13 @@ return oskWidth; } - // Query KMW if keyboardID is a keyboard with chiral modifiers. - // If keyboardID not given, use active keyboard - function setIsChiral(keyboardID) { - var isChiral = false; - if (typeof keyboardID !== "undefined") { - isChiral = tavultesoft.keymanweb.isChiral(keyboardID); - } else { - isChiral = tavultesoft.keymanweb.isChiral(); - } - window.console.log('Keyboard setIsChiral = ' + isChiral); + // Query KMW if a given keyboard uses chiral modifiers. + function setIsChiral(keyboardProperties) { + var isChiral = tavultesoft.keymanweb.isChiral(); + window.console.log('setIsChiral = ' + isChiral); + window.jsInterface.setIsChiral(isChiral); + return true; } function setKeymanLanguage(keyboardName, internalName, languageName, langId, version, font, oskFont) { From 086e7c511d2df68f355b7954b346e6ace01a9b22 Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Wed, 4 Oct 2017 08:35:01 +0700 Subject: [PATCH 17/19] Tweaked keyboard.html a bit. --- android/KMEA/app/src/main/assets/keyboard.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/android/KMEA/app/src/main/assets/keyboard.html b/android/KMEA/app/src/main/assets/keyboard.html index 83f9ac8250..c3e346018c 100644 --- a/android/KMEA/app/src/main/assets/keyboard.html +++ b/android/KMEA/app/src/main/assets/keyboard.html @@ -27,6 +27,7 @@ kmw['getOskHeight'] = getOskHeight; kmw['getOskWidth'] = getOskWidth; kmw['setActiveElement']('ta'); + ta.readOnly = false; kmw.addEventListener('keyboardloaded', setIsChiral); @@ -61,7 +62,10 @@ // Query KMW if a given keyboard uses chiral modifiers. function setIsChiral(keyboardProperties) { - var isChiral = tavultesoft.keymanweb.isChiral(); + var name = typeof(keyboardProperties.internalName) == "undefined" ? keyboardProperties.keyboardName : keyboardProperties.internalName; + + var isChiral = tavultesoft.keymanweb.isChiral(name); + window.console.log('For keyboard "' + name + '"'); window.console.log('setIsChiral = ' + isChiral); window.jsInterface.setIsChiral(isChiral); From 16048e5573d68c2e32723d6d578da313d8e4b9a0 Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Wed, 4 Oct 2017 09:10:16 +0700 Subject: [PATCH 18/19] setIsChiral() is now operational. --- android/KMEA/app/src/main/assets/keyboard.html | 1 - web/source/keymanweb.js | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/android/KMEA/app/src/main/assets/keyboard.html b/android/KMEA/app/src/main/assets/keyboard.html index c3e346018c..e825694530 100644 --- a/android/KMEA/app/src/main/assets/keyboard.html +++ b/android/KMEA/app/src/main/assets/keyboard.html @@ -63,7 +63,6 @@ // Query KMW if a given keyboard uses chiral modifiers. function setIsChiral(keyboardProperties) { var name = typeof(keyboardProperties.internalName) == "undefined" ? keyboardProperties.keyboardName : keyboardProperties.internalName; - var isChiral = tavultesoft.keymanweb.isChiral(name); window.console.log('For keyboard "' + name + '"'); window.console.log('setIsChiral = ' + isChiral); diff --git a/web/source/keymanweb.js b/web/source/keymanweb.js index 6033930438..2825f33d46 100644 --- a/web/source/keymanweb.js +++ b/web/source/keymanweb.js @@ -3627,11 +3627,15 @@ if(!window['tavultesoft']['keymanweb']['initialized']) { /** * Function isChiral * Scope Public - * @param {Object=} k0 + * @param {string|Object=} k0 * @return {boolean} * Description Tests if the active keyboard (or optional argument) uses chiral modifiers. */ keymanweb.isChiral = keymanweb['isChiral'] = function(k0) { + if(typeof(k0) == "string") { + k0 = keymanweb._getKeyboardByID(k0); + } + return !!(keymanweb.getKeyboardModifierBitmask(k0) & keymanweb['osk'].modifierBitmasks.IS_CHIRAL); } From 066575629947742c27ad2410d62225f1849a5fd6 Mon Sep 17 00:00:00 2001 From: darcywong00 Date: Wed, 4 Oct 2017 10:27:54 +0700 Subject: [PATCH 19/19] Final cleanup Remove chirality test keyboard from main kmapro app Fix KMSample2 compilation --- .../src/main/assets/languages/chirality.js | 79 ------------------- .../java/com/tavultesoft/kmea/KMManager.java | 14 ---- .../com/keyman/kmsample2/SystemKeyboard.java | 3 +- 3 files changed, 2 insertions(+), 94 deletions(-) delete mode 100644 android/KMEA/app/src/main/assets/languages/chirality.js diff --git a/android/KMEA/app/src/main/assets/languages/chirality.js b/android/KMEA/app/src/main/assets/languages/chirality.js deleted file mode 100644 index ade1c50d02..0000000000 --- a/android/KMEA/app/src/main/assets/languages/chirality.js +++ /dev/null @@ -1,79 +0,0 @@ -KeymanWeb.KR(new Keyboard_chirality()); - -/** - * Please note that this is an experimental handwritten keyboard designed for initial testing of modifier chirality support for KeymanWeb. - * As it is handcoded, it uses multiple codesize optimization techniques that Keyman Developer simply does not and will not employ, and it - * will only work with KeymanWeb based implementations. (Not supported for Keyman Desktop or Keyman for Mac.) - * - * (This keyboard was written before Developer's implementation of chirality support and was used to bootstrap KeymanWeb chirality, serving - * as the initial wave of testing.) - */ - -function Keyboard_chirality() { - this.KI = "Keyboard_chirality"; - this.KN = "Development Chirality Test Keyboard"; - this.KV = { - KMBM: 0x001F, - F: ' 1em "Arial"', - K102: 0, - KLS: { 'default': new Array("`", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "", "", "", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\\", "", "", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "", "", "", "", "", "", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "", "", "", "", "", ""), - 'shift': new Array("~", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "", "", "", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{", "}", "|", "", "", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "", "", "", "", "", "", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "", "", "", "", "", ""), - 'leftctrl': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ʍ", "ə", "ɹ", "ð", "ʏ", "ʉ", "ɨ", "ɵ", "ʘ", "", "", "", "", "", "", "ɑ", "ʃ", "", "ɸ", "ɣ", "ɥ", "ɟ", "", "ɬ", "ː", "", "", "", "", "", "", "", "ʒ", "χ", "ç", "ʋ", "β", "ɲ", "", "", "", "", "", "", "", "", "", ""), - 'leftctrl-shift': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ʡ", "", "ɘ", "ʀ", "θ", "", "ɯ", "ɪ", "", "", "", "", "", "", "", "", "ɒ", "ᶘ", "", "", "ɢ", "ʜ", "", "", "ʟ", "ː", "", "", "", "", "", "", "", "ᶚ", "", "", "", "ʙ", "ɴ", "", "", "", "ʔ", "", "", "", "", "", ""), - 'leftalt': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ɛ", "ɽ", "ʈ", "ɥ", "ʊ", "", "ɔ", "", "", "", "", "", "", "", "æ", "", "ɖ", "", "", "ɦ", "ʝ", "", "ɭ", "", "", "", "", "", "", "", "", "ʐ", "", "ɕ", "", "", "ɳ", "", "", "", "", "", "", "", "", "", ""), - 'rightalt': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ʠ", "ɰ", "ɜ", "ɾ", "ƭ", "", "ʌ", "", "ø", "ƥ", "", "", "", "", "", "", "ɐ", "σ", "ɗ", "", "ɠ", "ħ", "ʄ", "ƙ", "ɮ", "", "", "", "", "", "", "", "", "ʑ", "", "ƈ", "", "ɓ", "ŋ", "ɱ", "", "", "", "", "", "", "", "", ""), - 'leftalt-shift': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ʢ", "", "œ", "ɻ", "", "", "", "ᵻ", "ɞ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ʎ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""), - 'rightalt-shift': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ɶ", "ʁ", "", "", "ᵾ", "ᵼ", "ɤ", "", "", "", "", "", "", "", "ᴂ", "", "", "", "ʛ", "ɧ", "", "", "ɺ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "") - } - }; - this.KH = ''; - this.KM = 0; - - this.dfltCodes = ["K_BKQUOTE","K_1","K_2","K_3","K_4","K_5","K_6","K_7","K_8","K_9","K_0", - "K_HYPHEN","K_EQUAL","K_*","K_*","K_*","K_Q","K_W","K_E","K_R","K_T", - "K_Y","K_U","K_I","K_O","K_P","K_LBRKT","K_RBRKT","K_BKSLASH","K_*", - "K_*","K_*","K_A","K_S","K_D","K_F","K_G","K_H","K_J","K_K","K_L", - "K_COLON","K_QUOTE","K_*","K_*","K_*","K_*","K_*","K_oE2", - "K_Z","K_X","K_C","K_V","K_B","K_N","K_M","K_COMMA","K_PERIOD", - "K_SLASH","K_*","K_*","K_*","K_*","K_*","K_SPACE"]; - - this.gs = function (t, e) { - return this.g0(t, e); - }; - this.g0 = function (t, e) { - var k = KeymanWeb, r = 0, m = 0; - - // Handwritten time! - var kls = this.KV.KLS; - - var layers = ['default', 'shift', 'leftctrl', 'leftctrl-shift', 'leftalt', 'rightalt', 'leftalt-shift', 'rightalt-shift']; - - // Maps keystrokes by base key-codes and array into the key symbols displayed in KLS. - for(var i = 0; i < layers.length; i++) { - // Obtain the modifier code to match for the selected layer. - // The following uses a non-public property potentially subject to change in the future. - var modCode = k.osk.modifierCodes['VIRTUAL_KEY'] | k.osk.getModifierState(layers[i]); - var layer = layers[i]; - - for(var key=0; key < kls[layer].length; key++) { - var keySymbol = this.dfltCodes[key]; - - if(keySymbol == "K_*") { - continue; - } else if(kls[layer][key] != '') { - if (k.KKM(e, modCode, k.osk.keyCodes[keySymbol])) { - r = m = 1; - if(k.KSM(e, k.osk.modifierCodes['CAPS'])) { - k.KO(0, t, kls[layer][key].toUpperCase()); - } else { - k.KO(0, t, kls[layer][key]); - } - return r; - } - } - } - } - - return r; - }; -} \ No newline at end of file diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java index 577774a219..653410c2d4 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java @@ -215,20 +215,6 @@ public final class KMManager { InAppKeyboard.setWebViewClient(new KMInAppKeyboardWebViewClient(appContext)); InAppKeyboard.addJavascriptInterface(new KMInAppKeyboardJSHandler(appContext), "jsInterface"); InAppKeyboard.loadKeyboard(); - - // TODO: Test code adding chirality keyboard that needs to be removed before merging - HashMap kbInfo = new HashMap(); - final String Chirality_KeyboardFont = "{\"family\":\"LatinWeb\",\"source\":[\"DejaVuSans.ttf\"]}"; - - kbInfo.put(KMManager.KMKey_KeyboardID, "chirality"); - kbInfo.put(KMManager.KMKey_LanguageID, "eng"); - kbInfo.put(KMManager.KMKey_KeyboardName, "Chirality Keyboard"); - kbInfo.put(KMManager.KMKey_LanguageName, "English"); - kbInfo.put(KMManager.KMKey_KeyboardVersion, "1.0"); - kbInfo.put(KMManager.KMKey_Font, Chirality_KeyboardFont); - addKeyboard(appContext, kbInfo); - Log.d("KMManager", "initInApp"); - // end of test code } } diff --git a/android/Samples/KMSample2/app/src/main/java/com/keyman/kmsample2/SystemKeyboard.java b/android/Samples/KMSample2/app/src/main/java/com/keyman/kmsample2/SystemKeyboard.java index 44d816364f..bccf7d2366 100644 --- a/android/Samples/KMSample2/app/src/main/java/com/keyman/kmsample2/SystemKeyboard.java +++ b/android/Samples/KMSample2/app/src/main/java/com/keyman/kmsample2/SystemKeyboard.java @@ -162,10 +162,11 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven // Handle Keyman keyboard loaded event here if needed // We can set our custom keyboard here int kbIndex = KMManager.getKeyboardIndex(this, "tamil99m", "tam"); + KMManager.setKeyboard(this, kbIndex); } @Override - public void onKeyboardChanged(String newKeyboard, KeyboardType keyboardType) { + public void onKeyboardChanged(String newKeyboard) { // Handle Keyman keyboard changed event here if needed }