diff --git a/HISTORY.md b/HISTORY.md index 44ebb2af0e..fed4a177fb 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,29 @@ # Keyman Version History +## 17.0.79 alpha 2023-03-28 + +* chore(linux): Revert "Run and ignore autopkgtests on s390x" (#8506) +* docs(linux): Update minimum required Ubuntu version (#8526) +* docs(linux): Update common questions (#8532) + +## 17.0.78 alpha 2023-03-27 + +* chore(linux): Remove python3-raven dependency (#8507) +* refactor(android/engine): Make KMKeyboardJSHandler a normal class (#8494) + +## 17.0.77 alpha 2023-03-25 + +* chore(common): use mac /usr/bin/stat rather than homebrew version (#8498) + +## 17.0.76 alpha 2023-03-24 + +* chore(linux): Run and ignore autopkgtests on s390x (#8492) +* refactor(android/engine): Consolidate dispatchKey (#8483) + +## 17.0.75 alpha 2023-03-23 + +* chore(common): define BUILDER_CONFIGURATION env var (#8496) + ## 17.0.74 alpha 2023-03-22 * chore(windows): update sentry-native to 0.6.0 (#8464) diff --git a/VERSION.md b/VERSION.md index 4dc77ece12..31498260bd 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -17.0.75 \ No newline at end of file +17.0.80 \ No newline at end of file diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardJSHandler.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardJSHandler.java index 7098f2c3dc..e073008982 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardJSHandler.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardJSHandler.java @@ -23,7 +23,7 @@ import com.keyman.engine.KMManager.KeyboardType; import com.keyman.engine.util.CharSequenceUtil; import com.keyman.engine.util.KMLog; -public abstract class KMKeyboardJSHandler { +public class KMKeyboardJSHandler { private Context context; private KMKeyboard k = null; private static int KM_VIBRATE_DURATION = 100; // milliseconds @@ -96,21 +96,13 @@ public abstract class KMKeyboardJSHandler { return; } - if (k.keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP && - (KMTextView.activeView == null || KMTextView.activeView.getClass() != KMTextView.class)) { - if (KMTextView.activeView == null && KMManager.isDebugMode()) { - Log.w(TAG, "insertText failed: activeView is null"); - } + if (!isInappKMTextViewValid(k.keyboardType)) { return; } - InputConnection ic = (k.keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP) ? - KMTextView.activeView.onCreateInputConnection(new EditorInfo()) : - KMManager.getInputMethodService().getCurrentInputConnection(); + InputConnection ic = KMManager.getInputConnection(k.keyboardType); if (ic == null) { - if (KMManager.isDebugMode()) { - Log.w(TAG, "insertText failed: InputConnection is null"); - } + KMLog.LogError(TAG, "insertText failed: InputConnection is null"); return; } @@ -150,7 +142,7 @@ public abstract class KMKeyboardJSHandler { } if (s.length() > 0 && s.charAt(0) == '\n') { - keyDownUp(KeyEvent.KEYCODE_ENTER); + keyDownUp(KeyEvent.KEYCODE_ENTER, 0); ic.endBatchEdit(); return; } @@ -205,15 +197,59 @@ public abstract class KMKeyboardJSHandler { } @JavascriptInterface - public abstract boolean dispatchKey(final int code, final int eventModifiers); + public boolean dispatchKey(final int code, final int eventModifiers) { + Handler mainLoop = new Handler(Looper.getMainLooper()); + mainLoop.post(new Runnable() { + public void run() { + if (k == null) { + KMLog.LogError(TAG, "dispatchKey failed: Keyboard is null"); + return; + } - private void keyDownUp(int keyEventCode) { + if (k.subKeysWindow != null) { + return; + } + + if (!isInappKMTextViewValid(k.keyboardType)) { + return; + } + + InputConnection ic = KMManager.getInputConnection(k.keyboardType); + if (ic == null) { + KMLog.LogError(TAG, "dispatchKey failed: InputConnection is null"); + return; + } + + k.dismissHelpBubble(); + k.setShouldShowHelpBubble(false); + + // Handle tab or enter since KMW didn't process it + if (KMManager.isDebugMode()) { + Log.d(TAG, "dispatchKey called with code: " + code + ", eventModifiers: " + eventModifiers); + } + if (code == KMScanCodeMap.scanCodeMap[KMScanCodeMap.KEY_TAB]) { + keyDownUp(KeyEvent.KEYCODE_TAB, eventModifiers); + } else if (code == KMScanCodeMap.scanCodeMap[KMScanCodeMap.KEY_ENTER]) { + keyDownUp(KeyEvent.KEYCODE_ENTER, eventModifiers); + } + } + }); + return true; + } + + private void keyDownUp(int keyEventCode, int eventModifiers) { if (k.keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP) { KMTextView textView = (KMTextView)KMTextView.activeView; - textView.keyDownUp(KeyEvent.KEYCODE_ENTER); + if (keyEventCode == KeyEvent.KEYCODE_TAB) { + KeyEvent event = new KeyEvent(0, 0, 0, KeyEvent.KEYCODE_TAB, 0, eventModifiers, 0, 0, 0); + textView.dispatchKeyEvent(event); + } else { + textView.keyDownUp(keyEventCode); + } } else if (k.keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) { - KMManager.getInputMethodService().getCurrentInputConnection().sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyEventCode)); - KMManager.getInputMethodService().getCurrentInputConnection().sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyEventCode)); + InputConnection ic = KMManager.getInputConnection(KeyboardType.KEYBOARD_TYPE_SYSTEM); + ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyEventCode)); + ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyEventCode)); } } @@ -291,4 +327,25 @@ public abstract class KMKeyboardJSHandler { return sequence; } + /** + * If the keyboard type is KEYBOARD_TYPE_INAPP, check if the KMTextView is valid. + * For KEYBOARD_TYPE_SYSTEM, returns true. + * @param keyboardType + * @return boolean - false if keyboard type is INAPP and KMTextView is invalid. Otherwise true + */ + private static boolean isInappKMTextViewValid(KeyboardType keyboardType) { + if (keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP) { + if (KMTextView.activeView == null) { + if (KMManager.isDebugMode()) { + Log.w(TAG, "activeView is null"); + } + return false; + } + if (KMTextView.activeView.getClass() != KMTextView.class) { + return false; + } + } + + return true; + } } diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java index 16944ac860..8ad90bf6cc 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java @@ -158,6 +158,8 @@ public final class KMManager { }; protected static InputMethodService IMService; + protected static InputConnection inappInputConnection; // Input Connection for InApp Keyboard + private static boolean debugMode = false; private static boolean shouldAllowSetKeyboard = true; private static boolean didCopyAssets = false; @@ -421,13 +423,11 @@ public final class KMManager { didCopyAssets = true; } - if (keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP) { - initInAppKeyboard(appContext); - } else if (keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) { - initSystemKeyboard(appContext); - } else { + if (keyboardType == KeyboardType.KEYBOARD_TYPE_UNDEFINED) { String msg = "Cannot initialize: Invalid keyboard type"; KMLog.LogError(TAG, msg); + } else { + initKeyboard(appContext, keyboardType); } JSONUtils.initialize(new File(getPackagesDir())); @@ -459,6 +459,26 @@ public final class KMManager { } public static InputMethodService getInputMethodService() { return IMService; } + /** + * Get the input connection based on the keyboard type. + * For InApp keyboard, save the connection to inappInputConnection + * @param {KeyboardType} keyboard + * @return InputConnection + */ + protected static InputConnection getInputConnection(KeyboardType keyboard) { + if (keyboard == KeyboardType.KEYBOARD_TYPE_INAPP) { + if (inappInputConnection == null) { + inappInputConnection = KMTextView.activeView.onCreateInputConnection(new EditorInfo()); + } + return inappInputConnection; + } else if (keyboard == KeyboardType.KEYBOARD_TYPE_SYSTEM && IMService != null) { + return IMService.getCurrentInputConnection(); + } + + KMLog.LogError(TAG, "Unable to determine input connection"); + return null; + } + public static boolean executeHardwareKeystroke(int code, int shift, int lstates, int eventModifiers) { if (SystemKeyboard != null) { return executeHardwareKeystroke(code, shift, KeyboardType.KEYBOARD_TYPE_SYSTEM, lstates, eventModifiers); @@ -609,36 +629,35 @@ public final class KMManager { return params; } - private static void initInAppKeyboard(Context appContext) { - if (InAppKeyboard == null) { + private static void initKeyboard(Context appContext, KeyboardType keyboardType) { + KMKeyboard keyboard = null; + KMKeyboardWebViewClient webViewClient = null; + + if (keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP && InAppKeyboard == null) { InAppKeyboard = new KMKeyboard(appContext, KeyboardType.KEYBOARD_TYPE_INAPP); - RelativeLayout.LayoutParams params = getKeyboardLayoutParams(); - InAppKeyboard.setLayoutParams(params); - InAppKeyboard.setVerticalScrollBarEnabled(false); - InAppKeyboard.setHorizontalScrollBarEnabled(false); - InAppKeyboardWebViewClient = new KMKeyboardWebViewClient(appContext, KeyboardType.KEYBOARD_TYPE_INAPP); - InAppKeyboard.setWebViewClient(InAppKeyboardWebViewClient); - InAppKeyboard.addJavascriptInterface(new KMInAppKeyboardJSHandler(appContext, InAppKeyboard), "jsInterface"); - InAppKeyboard.loadKeyboard(); - - setEngineWebViewVersionStatus(appContext, InAppKeyboard); - } - } - - private static void initSystemKeyboard(Context appContext) { - if (SystemKeyboard == null) { + InAppKeyboardWebViewClient = new KMKeyboardWebViewClient(appContext, keyboardType); + keyboard = InAppKeyboard; + webViewClient = InAppKeyboardWebViewClient; + } else if (keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM && SystemKeyboard == null) { SystemKeyboard = new KMKeyboard(appContext, KeyboardType.KEYBOARD_TYPE_SYSTEM); - RelativeLayout.LayoutParams params = getKeyboardLayoutParams(); - SystemKeyboard.setLayoutParams(params); - SystemKeyboard.setVerticalScrollBarEnabled(false); - SystemKeyboard.setHorizontalScrollBarEnabled(false); - SystemKeyboardWebViewClient = new KMKeyboardWebViewClient(appContext, KeyboardType.KEYBOARD_TYPE_SYSTEM); - SystemKeyboard.setWebViewClient(SystemKeyboardWebViewClient); - SystemKeyboard.addJavascriptInterface(new KMSystemKeyboardJSHandler(appContext, SystemKeyboard), "jsInterface"); - SystemKeyboard.loadKeyboard(); - - setEngineWebViewVersionStatus(appContext, SystemKeyboard); + SystemKeyboardWebViewClient = new KMKeyboardWebViewClient(appContext, keyboardType); + keyboard = SystemKeyboard; + webViewClient = SystemKeyboardWebViewClient; } + + if (keyboard == null) { + return; + } + + RelativeLayout.LayoutParams params = getKeyboardLayoutParams(); + keyboard.setLayoutParams(params); + keyboard.setVerticalScrollBarEnabled(false); + keyboard.setHorizontalScrollBarEnabled(false); + keyboard.setWebViewClient(webViewClient); + keyboard.addJavascriptInterface(new KMKeyboardJSHandler(appContext, keyboard), "jsInterface"); + keyboard.loadKeyboard(); + + setEngineWebViewVersionStatus(appContext, keyboard); } public static String getLanguagePredictionPreferenceKey(String langID) { @@ -1981,7 +2000,7 @@ public final class KMManager { InAppKeyboardShouldIgnoreSelectionChange = false; } else if (kbType == KeyboardType.KEYBOARD_TYPE_SYSTEM) { if (SystemKeyboard != null && SystemKeyboardWebViewClient.getKeyboardLoaded() && !SystemKeyboardShouldIgnoreSelectionChange) { - InputConnection ic = (IMService != null ? IMService.getCurrentInputConnection() : null); + InputConnection ic = getInputConnection(KeyboardType.KEYBOARD_TYPE_SYSTEM); if (ic != null) { ExtractedText icText = ic.getExtractedText(new ExtractedTextRequest(), 0); if (icText != null) { @@ -2226,89 +2245,4 @@ public final class KMManager { globeKeyState = GlobeKeyState.GLOBE_KEY_STATE_UP; } } - - private static final class KMInAppKeyboardJSHandler extends KMKeyboardJSHandler { - - KMInAppKeyboardJSHandler(Context context, KMKeyboard k) { - super(context, k); - } - private static final String HANDLER_TAG = "IAK: JS Handler"; - - @JavascriptInterface - public boolean dispatchKey(final int code, final int eventModifiers) { - Handler mainLoop = new Handler(Looper.getMainLooper()); - mainLoop.post(new Runnable() { - public void run() { - if (InAppKeyboard == null) { - KMLog.LogError(TAG, "dispatchKey failed: InAppKeyboard is null"); - return; - } - - if (InAppKeyboard.subKeysWindow != null || KMTextView.activeView == null || KMTextView.activeView.getClass() != KMTextView.class) { - if ((KMTextView.activeView == null) && isDebugMode()) { - Log.w(HANDLER_TAG, "dispatchKey failed: activeView is null"); - } - return; - } - - // Handle tab or enter since KMW didn't process it - KMTextView textView = (KMTextView) KMTextView.activeView; - if (code == KMScanCodeMap.scanCodeMap[KMScanCodeMap.KEY_TAB]) { - KeyEvent event = new KeyEvent(0, 0, 0, KeyEvent.KEYCODE_TAB, 0, eventModifiers, 0, 0, 0); - textView.dispatchKeyEvent(event); - } else if (code == KMScanCodeMap.scanCodeMap[KMScanCodeMap.KEY_ENTER]) { - KeyEvent event = new KeyEvent(0, 0, 0, KeyEvent.KEYCODE_ENTER, 0, eventModifiers, 0, 0, 0); - textView.dispatchKeyEvent(event); - } - } - }); - return true; - } - } - - private static final class KMSystemKeyboardJSHandler extends KMKeyboardJSHandler { - KMSystemKeyboardJSHandler(Context context, KMKeyboard k) { - super(context, k); - } - private static final String HANDLER_TAG = "SWK: JS Handler"; - - @JavascriptInterface - public boolean dispatchKey(final int code, final int eventModifiers) { - Handler mainLoop = new Handler(Looper.getMainLooper()); - mainLoop.post(new Runnable() { - public void run() { - if (SystemKeyboard == null) { - KMLog.LogError(TAG, "dispatchKey failed: SystemKeyboard is null"); - return; - } - - if (SystemKeyboard.subKeysWindow != null) { - return; - } - - InputConnection ic = IMService.getCurrentInputConnection(); - if (ic == null) { - if (isDebugMode()) { - Log.w(HANDLER_TAG, "insertText failed: InputConnection is null"); - } - return; - } - - SystemKeyboard.dismissHelpBubble(); - SystemKeyboard.setShouldShowHelpBubble(false); - - // Handle tab or enter since KMW didn't process it - Log.d(HANDLER_TAG, "dispatchKey called with code: " + code + ", eventModifiers: " + eventModifiers); - if (code == KMScanCodeMap.scanCodeMap[KMScanCodeMap.KEY_TAB]) { - KeyEvent event = new KeyEvent(0, 0, 0, KeyEvent.KEYCODE_TAB, 0, eventModifiers, 0, 0, 0); - ic.sendKeyEvent(event); - } else if (code == KMScanCodeMap.scanCodeMap[KMScanCodeMap.KEY_ENTER]) { - KeyEvent event = new KeyEvent(0, 0, 0, KeyEvent.KEYCODE_ENTER, 0, eventModifiers, 0, 0, 0); - ic.sendKeyEvent(event); - } - } - }); - return true; - } - } } diff --git a/docs/build/linux-ubuntu.md b/docs/build/linux-ubuntu.md index d5cb971f8c..153fc5ccbd 100644 --- a/docs/build/linux-ubuntu.md +++ b/docs/build/linux-ubuntu.md @@ -21,7 +21,7 @@ The following projects **cannot** be built on Linux: ## System Requirements -* Minimum Ubuntu version: Ubuntu 18.04 +* Minimum Ubuntu version: Ubuntu 20.04 Other Linux distributions will also work if appropriate dependencies are installed. diff --git a/linux/debian/changelog b/linux/debian/changelog index 9e6e7f550c..9f69cc9a12 100644 --- a/linux/debian/changelog +++ b/linux/debian/changelog @@ -1,7 +1,19 @@ +keyman (16.0.139-4) unstable; urgency=medium + + * debian/tests: Revert previous change and ignore s390x from autopkgtests + + -- Eberhard Beilharz Fri, 24 Mar 2023 16:05:07 +0100 + +keyman (16.0.139-3) unstable; urgency=medium + + * debian/tests: Run autopkgtests on s390x but immediately return + + -- Eberhard Beilharz Wed, 22 Mar 2023 19:25:02 +0100 + keyman (16.0.139-2) unstable; urgency=medium * Don't build on s390x because Keyman doesn't work on big-endian architectures - (upstream bug https://github.com/keymanapp/keyman/issues/5111) + (upstream bug https://github.com/keymanapp/keyman/issues/5111) -- Eberhard Beilharz Mon, 20 Mar 2023 19:54:44 +0100 diff --git a/linux/debian/control b/linux/debian/control index 58bb436217..6fdd2bc817 100644 --- a/linux/debian/control +++ b/linux/debian/control @@ -22,7 +22,7 @@ Build-Depends: python3-pil, python3-pip, python3-qrcode, - python3-sentry-sdk (>= 1.1) | python3-raven, + python3-sentry-sdk (>= 1.1), python3-requests, python3-requests-cache, python3-setuptools, @@ -85,7 +85,7 @@ Depends: keyman-engine, python3-bs4, python3-gi, - python3-sentry-sdk (>= 1.1) | python3-raven, + python3-sentry-sdk (>= 1.1), dbus-x11, ${misc:Depends}, ${python3:Depends}, diff --git a/linux/help/common/index.md b/linux/help/common/index.md index 7bbc820dd0..a25eb07da0 100644 --- a/linux/help/common/index.md +++ b/linux/help/common/index.md @@ -57,22 +57,28 @@ sudo dpkg --purge ibus-kmfl libkmfl ## Q. What Linux distros will Keyman work with? -**A.** Keyman runs on Debian, Ubuntu, Wasta Linux and can be compiled to run from source in most distributions. +**A.** Keyman runs on Debian, Ubuntu, Wasta Linux and can be compiled to run +from source in most distributions. **Note:** Keyman for Linux no longer supports Ubuntu 16.04 LTS (Xenial Xerus). +Keyman 16 was the last version that supports Ubuntu 18.04 LTS (Bionic Beaver). ## Q. Will my existing Windows Keyman keyboard work with Keyman for Linux? -**A.** Most keyboards will work without change. A small subset of keyboards require features which -are not yet available in Keyman for Linux. These features will be progressively implemented. +**A.** Most keyboards will work without change. A small subset of keyboards +require features which are not yet available in Keyman for Linux. These +features will be progressively implemented. ## Q. How can I disable automatically reporting errors? -**A.** If Keyman crashes, it will automatically send a report to the development team. This report -is anonymous and contains only technical details relating to the crash. It does not include keystroke -data or personally identifying data. If you don't want these automatic error reports to be sent you -can set the environment variable `KEYMAN_NOSENTRY`: +**A.** If Keyman crashes, it will automatically send a report to the development +team. This report is anonymous and contains only technical details relating to +the crash. It does not include keystroke data or personally identifying data. If +you don't want these automatic error reports to be sent you can set the +environment variable `KEYMAN_NOSENTRY` and start `km-config` from +the command line: ```bash export KEYMAN_NOSENTRY=1 +km-config ``` diff --git a/mac/Keyman4MacIM/write-download_info.sh b/mac/Keyman4MacIM/write-download_info.sh index 716caea1a9..d185185ffb 100755 --- a/mac/Keyman4MacIM/write-download_info.sh +++ b/mac/Keyman4MacIM/write-download_info.sh @@ -81,7 +81,7 @@ DOWNLOAD_INFO_FILEPATH="${DMG_FILEPATH}.download_info" if [[ ! -f "$DMG_FILEPATH" ]]; then builder_die "Cannot compute file size or MD5 for non-existent DMG file: $DMG_FILEPATH" fi -DMG_FILE_SIZE=$(stat -f"%z" "$DMG_FILEPATH") +DMG_FILE_SIZE=$(/usr/bin/stat -f"%z" "$DMG_FILEPATH") DMG_MD5=$(md5 -q "$DMG_FILEPATH") if [[ -f "$DOWNLOAD_INFO_FILEPATH" ]]; then diff --git a/resources/shellHelperFunctions.sh b/resources/shellHelperFunctions.sh index d7612c74f5..3a05faaa8b 100755 --- a/resources/shellHelperFunctions.sh +++ b/resources/shellHelperFunctions.sh @@ -126,7 +126,7 @@ write_download_info() { FILE_EXTENSION="${BASE_FILE##*.}" - FILE_SIZE=$(stat -f"%z" "${BASE_PATH}/${BASE_FILE}") + FILE_SIZE=$(/usr/bin/stat -f"%z" "${BASE_PATH}/${BASE_FILE}") MD5_HASH=$(md5 -q "${BASE_PATH}/${BASE_FILE}") if [[ -f "$DOWNLOAD_INFO_FILEPATH" ]]; then