From 33c69969b5a9d976b7b4ecef60536e6a4c0b7ec8 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Thu, 16 Nov 2023 15:43:53 +0700 Subject: [PATCH 1/3] change(android): drops redundant setLayoutParams call --- android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java | 2 -- 1 file changed, 2 deletions(-) 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 5d47ab8ce4a..3f9a502918b 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 @@ -747,12 +747,10 @@ public static void onConfigurationChanged(Configuration newConfig) { // KMKeyboard if (InAppKeyboard != null) { RelativeLayout.LayoutParams params = getKeyboardLayoutParams(); - InAppKeyboard.setLayoutParams(params); InAppKeyboard.onConfigurationChanged(newConfig); } if (SystemKeyboard != null) { RelativeLayout.LayoutParams params = getKeyboardLayoutParams(); - SystemKeyboard.setLayoutParams(params); SystemKeyboard.onConfigurationChanged(newConfig); } } From c5b3bafcc7abec1677c0a84c59cdee0957d50615 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Fri, 17 Nov 2023 09:54:40 +0700 Subject: [PATCH 2/3] change(android): host-page init now retrieves current stub before KMW init --- .../KMEA/app/src/main/assets/android-host.js | 4 + .../keyman/engine/KMKeyboardJSHandler.java | 17 ++++ .../java/com/keyman/engine/data/Keyboard.java | 87 +++++++++++++++++++ web/src/engine/main/src/keyboardInterface.ts | 18 ++-- 4 files changed, 120 insertions(+), 6 deletions(-) diff --git a/android/KMEA/app/src/main/assets/android-host.js b/android/KMEA/app/src/main/assets/android-host.js index afa2825e33d..543e82dca4e 100644 --- a/android/KMEA/app/src/main/assets/android-host.js +++ b/android/KMEA/app/src/main/assets/android-host.js @@ -31,6 +31,10 @@ function init() { keyman.getOskHeight = getOskHeight; keyman.getOskWidth = getOskWidth; keyman.beepKeyboard = beepKeyboard; + + // Readies the keyboard stub for instant loading during the init process. + KeymanWeb.registerStub(JSON.parse(jsInterface.initialKeyboard())); + keyman.init({ 'embeddingApp':device, 'fonts':'packages/', 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 6c959b0056c..a8c5291f365 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 @@ -1,6 +1,7 @@ package com.keyman.engine; import android.content.Context; +import android.content.SharedPreferences; import android.os.Build; import android.os.Handler; import android.os.Looper; @@ -20,6 +21,7 @@ import static android.content.Context.VIBRATOR_SERVICE; import com.keyman.engine.KMManager.KeyboardType; +import com.keyman.engine.data.Keyboard; import com.keyman.engine.util.CharSequenceUtil; import com.keyman.engine.util.KMLog; @@ -62,6 +64,21 @@ public int getKeyboardWidth() { return kbWidth; } + @JavascriptInterface + public String initialKeyboard() { + // Note: KMManager.getCurrentKeyboard() (and similar) will throw errors until the host-page is first fully + // loaded and has set a keyboard. To allow the host-page to have earlier access, we instead get the stored + // keyboard index directly. + SharedPreferences prefs = context.getSharedPreferences(context.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE); + int index = prefs.getInt(KMManager.KMKey_UserKeyboardIndex, 0); + if (index < 0) { + index = 0; + } + + Keyboard kbd = KMManager.getKeyboardInfo(this.context, index); + return kbd.toStub(context); + } + // This annotation is required in Jelly Bean and later: @JavascriptInterface public void beepKeyboard() { diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/data/Keyboard.java b/android/KMEA/app/src/main/java/com/keyman/engine/data/Keyboard.java index 984284fe095..0dbfe5d0239 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/data/Keyboard.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/data/Keyboard.java @@ -16,11 +16,13 @@ import com.keyman.engine.util.KMLog; import com.keyman.engine.util.KMString; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.File; import java.io.Serializable; +import java.util.ArrayList; public class Keyboard extends LanguageResource implements Serializable { private static final String TAG = "Keyboard"; @@ -171,6 +173,91 @@ public JSONObject toJSON() { return o; } + private String getKeyboardRoot(Context context) { + String keyboardRoot = context.getDir("data", Context.MODE_PRIVATE).toString() + + File.separator; + + if (packageID.equals(KMManager.KMDefault_UndefinedPackageID)) { + return keyboardRoot + KMManager.KMDefault_UndefinedPackageID + File.separator; + } else { + return keyboardRoot + KMManager.KMDefault_AssetPackages + File.separator + packageID + File.separator; + } + } + + public String getKeyboardPath(Context context) { + String keyboardID = this.getKeyboardID(); + String keyboardVersion = this.getVersion(); + if (packageID.equals(KMManager.KMDefault_UndefinedPackageID)) { + return getKeyboardRoot(context) + keyboardID + "-" + keyboardVersion + ".js"; + } else { + return getKeyboardRoot(context) + keyboardID + ".js"; + } + } + + public String toStub(Context context) { + JSONObject stubObj = new JSONObject(); + + try { + stubObj.put("KN", this.getKeyboardName()); + stubObj.put("KI", "Keyboard_" + this.getKeyboardID()); + stubObj.put("KLC", this.getLanguageID()); + stubObj.put("KL", this.getLanguageName()); + stubObj.put("KF", this.getKeyboardPath(context)); + stubObj.put("KP", this.getPackageID()); + + String displayFont = this.getFont(); + if(displayFont != null) { + stubObj.put("KFont", this.buildDisplayFontObject(displayFont, context)); + } + + String oskFont = this.getOSKFont(); + if(oskFont != null) { + stubObj.put("KOskFont", this.buildDisplayFontObject(oskFont, context)); + } + + String displayName = this.getDisplayName(); + if(displayName != null) { + stubObj.put("displayName", displayName); + } + + return stubObj.toString(); + } catch(JSONException e) { + KMLog.LogException(TAG, "", e); + return null; + } + } + + /** + * Take a font JSON object and adjust to pass to JS + * 1. Replace "source" keys for "files" keys + * 2. Create full font paths for .ttf or .svg + * @param font String font JSON object as a string + * @return JSONObject of modified font information with full paths. If font is invalid, return `null` + */ + private JSONObject buildDisplayFontObject(String font, Context context) { + if(font == null || font.equals("")) { + return null; + } + + String keyboardRoot = this.getKeyboardRoot(context); + + try { + if (FileUtils.hasFontExtension(font)) { + JSONObject jfont = new JSONObject(); + jfont.put(KMManager.KMKey_FontFamily, font.substring(0, font.length() - 4)); + JSONArray jfiles = new JSONArray(); + jfiles.put(keyboardRoot + font); + jfont.put(KMManager.KMKey_FontFiles, jfiles); + return jfont; + } else { + return null; + } + } catch (JSONException e) { + KMLog.LogException(TAG, "Failed to make font for '"+font+"'", e); + return null; + } + } + /** * Get the fallback keyboard. If never specified, use sil_euro_latin * @param context Context diff --git a/web/src/engine/main/src/keyboardInterface.ts b/web/src/engine/main/src/keyboardInterface.ts index 305373aea32..971bcdcab5b 100644 --- a/web/src/engine/main/src/keyboardInterface.ts +++ b/web/src/engine/main/src/keyboardInterface.ts @@ -88,15 +88,21 @@ export default class KeyboardInterface { + const pathConfig = this.engine.config.paths; + return new KeyboardStub(Pstub, pathConfig.keyboards, pathConfig.fonts); + }; if(!this.engine.config.deferForInitialization.hasFinalized) { - this.engine.config.deferForInitialization.then(() => this.engine.keyboardRequisitioner.cache.addStub(stub)); + // pathConfig is not ready until KMW initializes, which prevents proper stub-building. + this.engine.config.deferForInitialization.then(() => this.engine.keyboardRequisitioner.cache.addStub(buildStub())); } else { + const stub = buildStub(); + + if(this.engine.keyboardRequisitioner?.cache.findMatchingStub(stub)) { + return 1; + } this.engine.keyboardRequisitioner.cache.addStub(stub); } From 7c9061e869be3b52c19f1d13d82d29189b04699a Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Fri, 17 Nov 2023 10:31:27 +0700 Subject: [PATCH 3/3] chore(android): remove fixed TODO --- .../KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java index e1c2b82a29a..22981302536 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java @@ -243,10 +243,8 @@ public boolean onConsoleMessage(ConsoleMessage cm) { } // Send console errors to Sentry in case they're missed by KMW sentryManager - // (Ignoring spurious message "No keyboard stubs exist = ...") - // TODO: Fix base error rather than trying to ignore it "No keyboard stubs exist" - if ((cm.messageLevel() == ConsoleMessage.MessageLevel.ERROR) && (!cm.message().startsWith("No keyboard stubs exist"))) { + if (cm.messageLevel() == ConsoleMessage.MessageLevel.ERROR) { // Make Toast notification of error and send log about falling back to default keyboard (ignore language ID) // Sanitize sourceId info String NAVIGATION_PATTERN = "^(.*)?(keyboard\\.html#[^-]+)-.*$";