From d0e16496304a877933cd78661e73fa17f9857df4 Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Wed, 8 Jul 2026 12:11:00 +0200 Subject: [PATCH 1/4] chore(android): cleanup font variable names, remove obsolete code This change renames the font variable names so that they match the two conceptual fonts we have. Also remove obsolete code that dealt with the `font` parameter being a JSON object instead of a string containing the font filename. Having the `font` parameter containing a JSON object was legacy code that won't happen with current Keyman versions. Follow-up-of: #16188 Build-bot: release:android --- .../java/com/keyman/engine/KMKeyboard.java | 122 ++++++------------ android/docs/engine/KMManager/setKeyboard.md | 12 +- 2 files changed, 46 insertions(+), 88 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 212de6d0a0e..7ee0dd471ee 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 @@ -84,7 +84,7 @@ final class KMKeyboard extends WebView { */ protected static KMManager.BannerType currentBanner = KMManager.BannerType.HTML; - private static String txtFontPath = ""; + private static String textFontPath = ""; private static String oskFontPath = ""; private final String fontUndefined = "undefined"; private GestureDetector gestureDetector; @@ -549,7 +549,7 @@ protected void toggleSuggestionBanner(HashMap associatedLexicalM * @return String */ public static String textFontFilename() { - return txtFontPath; + return textFontPath; } /** @@ -660,15 +660,15 @@ public boolean prepareKeyboardSwitch(String packageID, String keyboardID, String } public boolean setKeyboard(String packageID, String keyboardID, String languageID, - String keyboardName, String languageName, String kFont, - String kOskFont) { + String keyboardName, String languageName, String textFontName, + String oskFontName) { return setKeyboard(packageID, keyboardID, languageID, keyboardName, languageName, - kFont, kOskFont, null); + textFontName, oskFontName, null); } public boolean setKeyboard(String packageID, String keyboardID, String languageID, - String keyboardName, String languageName, String kFont, - String kOskFont, String displayName) { + String keyboardName, String languageName, String textFontName, + String oskFontName, String displayName) { if (packageID == null || keyboardID == null || languageID == null || keyboardName == null || languageName == null) { return false; } @@ -691,8 +691,8 @@ public boolean setKeyboard(String packageID, String keyboardID, String languageI languageID = kbInfo.getLanguageID(); keyboardName = kbInfo.getKeyboardName(); languageName = kbInfo.getLanguageName(); - kFont = kbInfo.getFont(); - kOskFont = kbInfo.getOSKFont(); + textFontName = kbInfo.getFont(); + oskFontName = kbInfo.getOSKFont(); retVal = false; // Keyboard changed, so determine version again @@ -700,14 +700,14 @@ public boolean setKeyboard(String packageID, String keyboardID, String languageI KMManager.getLatestKeyboardFileVersion(getContext(), packageID, keyboardID) : null; } - if(kOskFont == null || kOskFont.isEmpty()) - kOskFont = kFont; + if(oskFontName == null || oskFontName.isEmpty()) + oskFontName = textFontName; - JSONObject jDisplayFont = makeFontObject(kFont, packageID); - JSONObject jOskFont = makeFontObject(kOskFont, packageID); + JSONObject textFont = makeFontObject(textFontName, packageID); + JSONObject oskFont = makeFontObject(oskFontName, packageID); - txtFontPath = getFontFilename(kFont, packageID); - oskFontPath = getFontFilename(kOskFont, packageID); + textFontPath = getFontFilename(textFontName, packageID); + oskFontPath = getFontFilename(oskFontName, packageID); String kbKey = KMString.format("%s_%s", languageID, keyboardID); @@ -722,8 +722,8 @@ public boolean setKeyboard(String packageID, String keyboardID, String languageI reg.put("KF", keyboardUrl); reg.put("KP", packageID); - if (jDisplayFont != null) reg.put("KFont", jDisplayFont); - if (jOskFont != null) reg.put("KOskFont", jOskFont); + if (textFont != null) reg.put("KFont", textFont); + if (oskFont != null) reg.put("KOskFont", oskFont); if (displayName != null) reg.put("displayName", displayName); } catch(JSONException e) { KMLog.LogException(TAG, "", e); @@ -928,7 +928,7 @@ private void saveCurrentKeyboardIndex() { * Return the full path to the font file. If the font is invalid, return empty string. * @param font String - Font filename * @param packageID String - Package ID - * @return String - Full path to the font file. If font is invalid, return "". + * @return String - Full local path to the font file. If font is invalid, return "". */ private String getFontFilename(String font, String packageID) { if(font == null || font.equals("")) { @@ -1036,81 +1036,39 @@ public void onDismiss() { } /** - * Create a JSON object consisting of the font family and the URLs of the - * font files on the local device. + * Create a JSON object consisting of the font family and the URL of the + * font file on the local device. * - * The `font` parameter can either be the filename of the font (with an - * extension recognized as font), or a Font object or JSON string. - * In the former case a new JSON object is created with the font family - * derived from the filename, and the font filename prefixed with path - * to the fonts. - * In the latter case the legacy `sources` key is renamed to `files`. - * If `files` is a single string it will be prefixed with the path to the - * fonts. If `files` is an array, the array is iterated until finding - * the first file with a font extension which is then prefixed with the - * path to the fonts. + * @param fontFileName A string containing the font filename (with an + * extension recognized as font) + * @param packageID The package ID of the keyboard * - * @param font A string containing either the font filename or a font - * JSON object as a string - * @param packageID The package ID of the keyboard - * - * @return JSONObject of modified font information with full paths. If font + * @return JSONObject of modified font information with full path. If fontFileName * is invalid, return `null`. */ - private JSONObject makeFontObject(String font, String packageID) { + private JSONObject makeFontObject(String fontFileName, String packageID) { - if(font == null || font.equals("")) { + if(fontFileName == null || fontFileName.equals("")) { return null; } - try { - if (FileUtils.hasFontExtension(font)) { - JSONObject jfont = new JSONObject(); - jfont.put(KMManager.KMKey_FontFamily, font.substring(0, font.length()-4)); - JSONArray jfiles = new JSONArray(); - String fontRoot = KMManager.isDefaultFont(font) ? getDataRootUrl() : getPackageRootUrl(packageID); - jfiles.put(fontRoot + font); - jfont.put(KMManager.KMKey_FontFiles, jfiles); - return jfont; - } - - // REVIEW: Why do we need the complicated code below? Can this still - // happen, or can we remove it? (see also getFontFilename) - KMLog.LogInfo(TAG, "Got font without font extension: " + font); - - JSONObject fontObj = new JSONObject(font); - - // Replace "sources" key with "files" - if (fontObj.has(KMManager.KMKey_FontSource)) { - fontObj.put(KMManager.KMKey_FontFiles, fontObj.get(KMManager.KMKey_FontSource)); - fontObj.remove(KMManager.KMKey_FontSource); - } + if (!FileUtils.hasFontExtension(fontFileName)) { + KMLog.LogInfo(TAG, "makeFontObject: Got font without font extension: " + fontFileName); + return null; + } - Object obj = fontObj.get(KMManager.KMKey_FontFiles); - if (obj instanceof String) { - String fontFile = fontObj.getString(KMManager.KMKey_FontFiles); - String fontRoot = KMManager.isDefaultFont(fontFile) ? getDataRootUrl() : getPackageRootUrl(packageID); - fontObj.put(KMManager.KMKey_FontFiles, fontRoot + obj); - return fontObj; - } else if (obj instanceof JSONArray) { - JSONArray sourceArray = fontObj.optJSONArray(KMManager.KMKey_FontFiles); - if (sourceArray != null) { - for (int i = 0; i < sourceArray.length(); i++) { - String fontFile = sourceArray.getString(i); - if (FileUtils.hasFontExtension(fontFile)) { - String fontRoot = KMManager.isDefaultFont(fontFile) ? getDataRootUrl() : getPackageRootUrl(packageID); - fontObj.put(KMManager.KMKey_FontFiles, fontRoot + fontFile); - fontObj.remove(KMManager.KMKey_FontSource); - return fontObj; - } - } - } - } + try { + JSONObject font = new JSONObject(); + font.put(KMManager.KMKey_FontFamily, fontFileName.substring(0, fontFileName.length()-4)); + JSONArray files = new JSONArray(); + String fontRoot = KMManager.isDefaultFont(fontFileName) ? getDataRootUrl() : getPackageRootUrl(packageID); + files.put(fontRoot + fontFileName); + font.put(KMManager.KMKey_FontFiles, files); + return font; } catch (JSONException e) { - KMLog.LogException(TAG, "Failed to make font for '"+font+"'", e); + KMLog.LogException(TAG, "Failed to make font for '"+fontFileName+"'", e); + return null; } - - return null; } protected void showHelpBubble() { diff --git a/android/docs/engine/KMManager/setKeyboard.md b/android/docs/engine/KMManager/setKeyboard.md index 4a152f2480d..38852486e92 100644 --- a/android/docs/engine/KMManager/setKeyboard.md +++ b/android/docs/engine/KMManager/setKeyboard.md @@ -35,8 +35,8 @@ normally returned by [`getKeyboardInfo()`](getKeyboardInfo). ```java KMManager.setKeyboard(String packageID, String keyboardID, String languageID) -KMManager.setKeyboard(String packageID, String keyboardID, String languageID, String keyboardName, String languageName, String kFont, String kOskFont) -KMManager.setKeyboard(String packageID, String keyboardID, String languageID, String keyboardName, String languageName, String kFont, String kOskFont, String displayName) +KMManager.setKeyboard(String packageID, String keyboardID, String languageID, String keyboardName, String languageName, String textFont, String oskFont) +KMManager.setKeyboard(String packageID, String keyboardID, String languageID, String keyboardName, String languageName, String textFont, String oskFont, String displayName) ``` ### Parameters @@ -56,12 +56,12 @@ KMManager.setKeyboard(String packageID, String keyboardID, String languageID, St `languageName` : Name of the associated language. -`kFont` -: Filename or description of the font to type with the keyboard. Can be `null` +`textFont` +: Filename of the font to type with the keyboard. Can be `null` or empty string. -`kOskFont` -: Filename or description of the font displayed on the keyboard. Can be `null` +`oskFont` +: Filename of the font displayed on the keyboard. Can be `null` or empty string. `displayName` From 574e7f6838593591092d52666724fcc0e95273ee Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Thu, 9 Jul 2026 17:24:55 +0200 Subject: [PATCH 2/4] chore(android): address code review comments Co-authored-by: Marc Durdin --- .../java/com/keyman/engine/KMKeyboard.java | 26 +++++++++---------- android/docs/engine/KMManager/setKeyboard.md | 10 +++---- 2 files changed, 18 insertions(+), 18 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 7ee0dd471ee..ca9edff90b5 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 @@ -660,15 +660,15 @@ public boolean prepareKeyboardSwitch(String packageID, String keyboardID, String } public boolean setKeyboard(String packageID, String keyboardID, String languageID, - String keyboardName, String languageName, String textFontName, - String oskFontName) { + String keyboardName, String languageName, String textFontFilename, + String oskFontFilename) { return setKeyboard(packageID, keyboardID, languageID, keyboardName, languageName, - textFontName, oskFontName, null); + textFontFilename, oskFontFilename, null); } public boolean setKeyboard(String packageID, String keyboardID, String languageID, - String keyboardName, String languageName, String textFontName, - String oskFontName, String displayName) { + String keyboardName, String languageName, String textFontFilename, + String oskFontFilename, String displayName) { if (packageID == null || keyboardID == null || languageID == null || keyboardName == null || languageName == null) { return false; } @@ -691,8 +691,8 @@ public boolean setKeyboard(String packageID, String keyboardID, String languageI languageID = kbInfo.getLanguageID(); keyboardName = kbInfo.getKeyboardName(); languageName = kbInfo.getLanguageName(); - textFontName = kbInfo.getFont(); - oskFontName = kbInfo.getOSKFont(); + textFontFilename = kbInfo.getFont(); + oskFontFilename = kbInfo.getOSKFont(); retVal = false; // Keyboard changed, so determine version again @@ -700,14 +700,14 @@ public boolean setKeyboard(String packageID, String keyboardID, String languageI KMManager.getLatestKeyboardFileVersion(getContext(), packageID, keyboardID) : null; } - if(oskFontName == null || oskFontName.isEmpty()) - oskFontName = textFontName; + if(oskFontFilename == null || oskFontFilename.isEmpty()) + oskFontFilename = textFontFilename; - JSONObject textFont = makeFontObject(textFontName, packageID); - JSONObject oskFont = makeFontObject(oskFontName, packageID); + JSONObject textFont = makeFontObject(textFontFilename, packageID); + JSONObject oskFont = makeFontObject(oskFontFilename, packageID); - textFontPath = getFontFilename(textFontName, packageID); - oskFontPath = getFontFilename(oskFontName, packageID); + textFontPath = getFontFilename(textFontFilename, packageID); + oskFontPath = getFontFilename(oskFontFilename, packageID); String kbKey = KMString.format("%s_%s", languageID, keyboardID); diff --git a/android/docs/engine/KMManager/setKeyboard.md b/android/docs/engine/KMManager/setKeyboard.md index 38852486e92..8faafae31de 100644 --- a/android/docs/engine/KMManager/setKeyboard.md +++ b/android/docs/engine/KMManager/setKeyboard.md @@ -35,8 +35,8 @@ normally returned by [`getKeyboardInfo()`](getKeyboardInfo). ```java KMManager.setKeyboard(String packageID, String keyboardID, String languageID) -KMManager.setKeyboard(String packageID, String keyboardID, String languageID, String keyboardName, String languageName, String textFont, String oskFont) -KMManager.setKeyboard(String packageID, String keyboardID, String languageID, String keyboardName, String languageName, String textFont, String oskFont, String displayName) +KMManager.setKeyboard(String packageID, String keyboardID, String languageID, String keyboardName, String languageName, String textFontFilename, String oskFontFilename) +KMManager.setKeyboard(String packageID, String keyboardID, String languageID, String keyboardName, String languageName, String textFontFilename, String oskFontFilename, String displayName) ``` ### Parameters @@ -56,13 +56,13 @@ KMManager.setKeyboard(String packageID, String keyboardID, String languageID, St `languageName` : Name of the associated language. -`textFont` +`textFontFilename` : Filename of the font to type with the keyboard. Can be `null` or empty string. -`oskFont` +`oskFontFilename` : Filename of the font displayed on the keyboard. Can be `null` - or empty string. + or empty string, and if `null` or empty string, uses `textFontFilename`. `displayName` : A text string to display on the spacebar identifying this keyboard; if `null`, From 82ab6477e54b89851bd1f628449daabc8e5b380a Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Mon, 13 Jul 2026 19:15:10 +0200 Subject: [PATCH 3/4] chore(android): keep handling of legacy keyboards Reverts part of commit d0e16496304a877933cd78661e73fa17f9857df4 until we know if there are still users out there that are using legacy cloud keyboards. See #16237. Build-bot: skip build:android --- .../java/com/keyman/engine/KMKeyboard.java | 86 ++++++++++++++----- 1 file changed, 64 insertions(+), 22 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 ca9edff90b5..94c1aaa2aee 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 @@ -1036,39 +1036,81 @@ public void onDismiss() { } /** - * Create a JSON object consisting of the font family and the URL of the - * font file on the local device. + * Create a JSON object consisting of the font family and the URLs of the + * font files on the local device. * - * @param fontFileName A string containing the font filename (with an - * extension recognized as font) - * @param packageID The package ID of the keyboard + * The `font` parameter can either be the filename of the font (with an + * extension recognized as font), or a Font object or JSON string. + * In the former case a new JSON object is created with the font family + * derived from the filename, and the font filename prefixed with path + * to the fonts. + * In the latter case the legacy `sources` key is renamed to `files`. + * If `files` is a single string it will be prefixed with the path to the + * fonts. If `files` is an array, the array is iterated until finding + * the first file with a font extension which is then prefixed with the + * path to the fonts. * - * @return JSONObject of modified font information with full path. If fontFileName + * @param font A string containing either the font filename or a font + * JSON object as a string + * @param packageID The package ID of the keyboard + * + * @return JSONObject of modified font information with full paths. If font * is invalid, return `null`. */ - private JSONObject makeFontObject(String fontFileName, String packageID) { - - if(fontFileName == null || fontFileName.equals("")) { - return null; - } + private JSONObject makeFontObject(String font, String packageID) { - if (!FileUtils.hasFontExtension(fontFileName)) { - KMLog.LogInfo(TAG, "makeFontObject: Got font without font extension: " + fontFileName); + if(font == null || font.equals("")) { return null; } try { - JSONObject font = new JSONObject(); - font.put(KMManager.KMKey_FontFamily, fontFileName.substring(0, fontFileName.length()-4)); - JSONArray files = new JSONArray(); - String fontRoot = KMManager.isDefaultFont(fontFileName) ? getDataRootUrl() : getPackageRootUrl(packageID); - files.put(fontRoot + fontFileName); - font.put(KMManager.KMKey_FontFiles, files); - return font; + if (FileUtils.hasFontExtension(font)) { + JSONObject jfont = new JSONObject(); + jfont.put(KMManager.KMKey_FontFamily, font.substring(0, font.length()-4)); + JSONArray jfiles = new JSONArray(); + String fontRoot = KMManager.isDefaultFont(font) ? getDataRootUrl() : getPackageRootUrl(packageID); + jfiles.put(fontRoot + font); + jfont.put(KMManager.KMKey_FontFiles, jfiles); + return jfont; + } + + // REVIEW: Why do we need the complicated code below? Can this still + // happen, or can we remove it? (see also getFontFilename) + KMLog.LogInfo(TAG, "Got font without font extension: " + font); + + JSONObject fontObj = new JSONObject(font); + + // Replace "sources" key with "files" + if (fontObj.has(KMManager.KMKey_FontSource)) { + fontObj.put(KMManager.KMKey_FontFiles, fontObj.get(KMManager.KMKey_FontSource)); + fontObj.remove(KMManager.KMKey_FontSource); + } + + Object obj = fontObj.get(KMManager.KMKey_FontFiles); + if (obj instanceof String) { + String fontFile = fontObj.getString(KMManager.KMKey_FontFiles); + String fontRoot = KMManager.isDefaultFont(fontFile) ? getDataRootUrl() : getPackageRootUrl(packageID); + fontObj.put(KMManager.KMKey_FontFiles, fontRoot + obj); + return fontObj; + } else if (obj instanceof JSONArray) { + JSONArray sourceArray = fontObj.optJSONArray(KMManager.KMKey_FontFiles); + if (sourceArray != null) { + for (int i = 0; i < sourceArray.length(); i++) { + String fontFile = sourceArray.getString(i); + if (FileUtils.hasFontExtension(fontFile)) { + String fontRoot = KMManager.isDefaultFont(fontFile) ? getDataRootUrl() : getPackageRootUrl(packageID); + fontObj.put(KMManager.KMKey_FontFiles, fontRoot + fontFile); + fontObj.remove(KMManager.KMKey_FontSource); + return fontObj; + } + } + } + } } catch (JSONException e) { - KMLog.LogException(TAG, "Failed to make font for '"+fontFileName+"'", e); - return null; + KMLog.LogException(TAG, "Failed to make font for '"+font+"'", e); } + + return null; } protected void showHelpBubble() { From 88731e51fc6df7ffcff5d7334b737690d90322c6 Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Mon, 13 Jul 2026 19:29:21 +0200 Subject: [PATCH 4/4] chore(android): more cleanup --- .../main/java/com/keyman/engine/KMKeyboard.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 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 94c1aaa2aee..af948f1a45d 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 @@ -926,22 +926,22 @@ private void saveCurrentKeyboardIndex() { /** * Return the full path to the font file. If the font is invalid, return empty string. - * @param font String - Font filename + * @param fontFilename String - Font filename * @param packageID String - Package ID - * @return String - Full local path to the font file. If font is invalid, return "". + * @return String - Full path to the font file. If fontFilename is invalid, return "". */ - private String getFontFilename(String font, String packageID) { - if(font == null || font.equals("")) { + private String getFontFilename(String fontFilename, String packageID) { + if(fontFilename == null || fontFilename.equals("")) { return ""; } - if (!FileUtils.hasFontExtension(font)) { + if (!FileUtils.hasFontExtension(fontFilename)) { // QUESTION: do we log this? return ""; } - String fontRoot = KMManager.isDefaultFont(font) ? getDataRootPath() : getPackageRootPath(packageID); - return fontRoot + font; + String fontRoot = KMManager.isDefaultFont(fontFilename) ? getDataRootPath() : getPackageRootPath(packageID); + return fontRoot + fontFilename; } @SuppressLint("InflateParams")