Merge pull request #16188 from keymanapp/fix/android/16187_font

fix(android): differentiate between fontPath and fontUrl in Keyman Engine for Android
This commit is contained in:
Eberhard Beilharz 2026-07-08 14:35:10 +02:00 committed by GitHub
commit fcf8988910
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 112 additions and 87 deletions

View file

@ -1,9 +1,10 @@
/**
* Copyright (C) 2017 SIL International. All rights reserved.
/*
* Keyman is copyright (C) SIL Global. MIT License.
*/
package com.tavultesoft.kmapro;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
@ -386,7 +387,7 @@ public class WebBrowserActivity extends BaseActivity {
super.onResume();
if (webView != null) {
if (didFinishLoading) {
String fontFilename = KMManager.getKeyboardTextFontFilename();
String fontFilename = getKeyboardTextFontFilenameOnly();
if (!loadedFont.equals(fontFilename)) {
webView.reload();
}
@ -432,8 +433,19 @@ public class WebBrowserActivity extends BaseActivity {
}
}
/**
* Returns the filename without path of the display font of the current keyboard.
*/
private String getKeyboardTextFontFilenameOnly() {
String fontPath = KMManager.getKeyboardTextFontFilename();
if (fontPath == null || fontPath.isEmpty()) {
return "";
}
return new File(fontPath).getName();
}
private void loadFont() {
String font = KMManager.getKeyboardTextFontFilename();
String font = getKeyboardTextFontFilenameOnly();
if (!font.isEmpty()) {
loadedFont = font;
String fontUrl = String.format("%s%s", fontBaseUri, font);

View file

@ -84,10 +84,8 @@ final class KMKeyboard extends WebView {
*/
protected static KMManager.BannerType currentBanner = KMManager.BannerType.HTML;
private static String txtFont = "";
private static String oskFont = null;
private static String dataRoot = "";
private static String packageRoot = "";
private static String txtFontPath = "";
private static String oskFontPath = "";
private final String fontUndefined = "undefined";
private GestureDetector gestureDetector;
private static ArrayList<OnKeyboardEventListener> kbEventListeners = null;
@ -551,7 +549,7 @@ final class KMKeyboard extends WebView {
* @return String
*/
public static String textFontFilename() {
return txtFont;
return txtFontPath;
}
/**
@ -559,10 +557,10 @@ final class KMKeyboard extends WebView {
* @return String
*/
public static String oskFontFilename() {
return oskFont;
return oskFontPath;
}
// REVIEW: this method seems to be unused
// REVIEW: this method seems to be unused and undocumented. Can we remove it?
/**
* Return the full path to the special OSK font,
* which is with all the keyboard assets at the root app_data folder
@ -648,8 +646,6 @@ final class KMKeyboard extends WebView {
}
String kbKey = KMString.format("%s_%s", languageID, keyboardID);
setPackageRoot(packageID);
// Escape single-quoted names for javascript call
keyboardName = keyboardName.replaceAll("\'", "\\\\'"); // Double-escaped-backslash b/c regex.
@ -704,16 +700,14 @@ final class KMKeyboard extends WebView {
KMManager.getLatestKeyboardFileVersion(getContext(), packageID, keyboardID) : null;
}
setPackageRoot(packageID);
if(kOskFont == null || kOskFont.isEmpty())
kOskFont = kFont;
JSONObject jDisplayFont = makeFontObject(kFont);
JSONObject jOskFont = makeFontObject(kOskFont);
JSONObject jDisplayFont = makeFontObject(kFont, packageID);
JSONObject jOskFont = makeFontObject(kOskFont, packageID);
txtFont = getFontFilename(jDisplayFont);
oskFont = getFontFilename(jOskFont);
txtFontPath = getFontFilename(kFont, packageID);
oskFontPath = getFontFilename(kOskFont, packageID);
String kbKey = KMString.format("%s_%s", languageID, keyboardID);
@ -810,26 +804,40 @@ final class KMKeyboard extends WebView {
}
}
// Set the base path of the keyboard depending on the package ID
private void setPackageRoot(String packageID) {
this.dataRoot = WebViewUtils.buildAssetUrl("");
/**
* Return the root URL for the data folder. Even though this is a local
* location this returns a URL with a magic domain so that it can be
* loaded with fetch() in the webview.
*/
private String getDataRootUrl() {
return WebViewUtils.buildAssetUrl("");
}
/**
* Return the root path for the data folder as a file path. This should be
* used where the file is not loaded through the webview, but is instead
* used by the app directly.
*/
private String getDataRootPath() {
return context.getDir("data", Context.MODE_PRIVATE).toString() + File.separator;
}
private String getPackageRootUrl(String packageID) {
if (packageID.equals(KMManager.KMDefault_UndefinedPackageID)) {
this.packageRoot = this.dataRoot + KMManager.KMDefault_UndefinedPackageID + "/";
} else {
this.packageRoot = this.dataRoot + KMManager.KMDefault_AssetPackages + "/" + packageID + "/";
return getDataRootUrl() + KMManager.KMDefault_UndefinedPackageID + "/";
}
return getDataRootUrl() + KMManager.KMDefault_AssetPackages + "/" + packageID + "/";
}
private String getDataRoot() {
return this.dataRoot;
}
private String getPackageRoot() {
return this.packageRoot;
private String getPackageRootPath(String packageID) {
if (packageID.equals(KMManager.KMDefault_UndefinedPackageID)) {
return getDataRootPath() + KMManager.KMDefault_UndefinedPackageID + File.separator;
}
return getDataRootPath() + KMManager.KMDefault_AssetPackages + File.separator + packageID + File.separator;
}
private String makeKeyboardUrl(String packageID, String keyboardID, String keyboardVersion) {
String keyboardUrl = getPackageRoot();
String keyboardUrl = getPackageRootUrl(packageID);
if (packageID.equals(KMManager.KMDefault_UndefinedPackageID)) {
keyboardUrl += keyboardID + "-" + keyboardVersion + ".js";
} else {
@ -917,42 +925,23 @@ final class KMKeyboard extends WebView {
}
/**
* getFontFilename
* Parse a Font JSON object and return the font filename (ending in .ttf or .otf)
* @param fontObj JSONObject - Font JSON object
* @return String - Filename for the font. If font is invalid, return ""
* 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 "".
*/
private String getFontFilename(JSONObject fontObj) {
String font = "";
if (fontObj == null) {
return font;
}
try {
JSONArray sourceArray = fontObj.optJSONArray(KMManager.KMKey_FontFiles);
if (sourceArray != null) {
String fontFile;
int length = sourceArray.length();
for (int i = 0; i < length; i++) {
fontFile = sourceArray.getString(i);
if (FileUtils.hasFontExtension(fontFile)) {
font = fontFile;
break;
}
}
} else {
String fontFile = fontObj.optString(KMManager.KMKey_FontFiles);
if (fontFile != null) {
if (FileUtils.hasFontExtension(fontFile)) {
font = fontFile;
}
}
}
} catch (JSONException e) {
KMLog.LogException(TAG, "", e);
font = "";
private String getFontFilename(String font, String packageID) {
if(font == null || font.equals("")) {
return "";
}
return font;
if (!FileUtils.hasFontExtension(font)) {
// QUESTION: do we log this?
return "";
}
String fontRoot = KMManager.isDefaultFont(font) ? getDataRootPath() : getPackageRootPath(packageID);
return fontRoot + font;
}
@SuppressLint("InflateParams")
@ -1061,12 +1050,14 @@ final class KMKeyboard extends WebView {
* the first file with a font extension which is then prefixed with the
* path to the fonts.
*
* @param font A string containing either the font filename or a font JSON
* object as a string
* @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 font) {
private JSONObject makeFontObject(String font, String packageID) {
if(font == null || font.equals("")) {
return null;
@ -1077,12 +1068,16 @@ final class KMKeyboard extends WebView {
JSONObject jfont = new JSONObject();
jfont.put(KMManager.KMKey_FontFamily, font.substring(0, font.length()-4));
JSONArray jfiles = new JSONArray();
String fontRoot = KMManager.isDefaultFont(font) ? getDataRoot() : getPackageRoot();
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"
@ -1094,7 +1089,7 @@ final class KMKeyboard extends WebView {
Object obj = fontObj.get(KMManager.KMKey_FontFiles);
if (obj instanceof String) {
String fontFile = fontObj.getString(KMManager.KMKey_FontFiles);
String fontRoot = KMManager.isDefaultFont(fontFile) ? getDataRoot() : getPackageRoot();
String fontRoot = KMManager.isDefaultFont(fontFile) ? getDataRootUrl() : getPackageRootUrl(packageID);
fontObj.put(KMManager.KMKey_FontFiles, fontRoot + obj);
return fontObj;
} else if (obj instanceof JSONArray) {
@ -1103,7 +1098,7 @@ final class KMKeyboard extends WebView {
for (int i = 0; i < sourceArray.length(); i++) {
String fontFile = sourceArray.getString(i);
if (FileUtils.hasFontExtension(fontFile)) {
String fontRoot = KMManager.isDefaultFont(fontFile) ? getDataRoot() : getPackageRoot();
String fontRoot = KMManager.isDefaultFont(fontFile) ? getDataRootUrl() : getPackageRootUrl(packageID);
fontObj.put(KMManager.KMKey_FontFiles, fontRoot + fontFile);
fontObj.remove(KMManager.KMKey_FontSource);
return fontObj;

View file

@ -1629,6 +1629,16 @@ public final class KMManager {
*/
public static Typeface getFontTypeface(Context context, String fontFilename) {
try {
if (fontFilename == null || fontFilename.isEmpty()) {
return null;
}
if (fontFilename.startsWith("http://") || fontFilename.startsWith("https://")
|| fontFilename.startsWith("file://")) {
// Font file is not local, so cannot load Typeface
KMLog.LogError(TAG, "Font file is not local: " + fontFilename);
return null;
}
if ((fontFilename != null) && FileUtils.hasFontExtension(fontFilename)) {
// Ignore .woff files if Android 7.0 / 7.1 (Issue #4896)
if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) &&

View file

@ -19,7 +19,7 @@ KMManager.getFontTypeface(Context context, String fontFilename)
: The context.
`fontFilename`
: The filename of the font.
: The filename and full path of the font.
### Returns
@ -29,7 +29,7 @@ if it exists, `null` otherwise.
## Description
Use this method to create a new typeface from the font file with
specified filename if it exists in `assets/fonts/` folder.
specified filename if it exists.
## Examples
@ -37,9 +37,10 @@ specified filename if it exists in `assets/fonts/` folder.
The following script illustrate the use of `getFontTypeface()`:
``` javascript
``` java
KMTextView textView = (KMTextView) findViewById(R.id.kmTextView);
Typeface fontTypeface = KMManager.getFontTypeface(this, "aava1.ttf");
String textFontFilename = KMManager.getKeyboardTextFontFilename();
Typeface fontTypeface = KMManager.getFontTypeface(this, textFontFilename);
textView.setTypeface(fontTypeface);
```

View file

@ -5,18 +5,25 @@ title: KMManager.getKeyboardOskFontFilename()
## Summary
The **`getKeyboardOskFontFilename()`** method returns the selected
keyboard's OSK font filename.
keyboard's OSK font filename and full path.
## Syntax
``` javascript
``` java
KMManager.getKeyboardOskFontFilename()
```
### Returns
Returns the selected keyboard's OSK font filename as `String` if it has
any, empty string otherwise.
Returns the selected keyboard's OSK font filename and full path as
`String` if it has any, empty string otherwise. Note that the
on-screen keyboard will fallback to the keyboard text font if
no OSK font is specified.
The OSK font should not be used for a text view, because some
OSK fonts are appropriate for use only in the on screen keyboard;
see [`&displayMap`](/developer/language/reference/displaymap) for
reference.
## Description
@ -29,8 +36,8 @@ Use this method to get the OSK font filename of the selected keyboard.
The following script illustrate the use of
`getKeyboardOskFontFilename()`:
``` javascript
String oskFontFilename = KMManager.getKeyboardOskFontFilename();
``` java
String oskFontFilename = KMManager.getKeyboardOskFontFilename();
```
## See also

View file

@ -5,18 +5,18 @@ title: KMManager.getKeyboardTextFontFilename()
## Summary
The **`getKeyboardTextFontFilename()`** method returns the selected
keyboard's text font filename.
keyboard's text font filename and full path.
## Syntax
``` javascript
``` java
KMManager.getKeyboardTextFontFilename()
```
### Returns
Returns the selected keyboard's text font filename as `String` if it has
any, empty string otherwise.
Returns the selected keyboard's text font filename and full path as
`String` if it has any, empty string otherwise.
## Description
@ -29,8 +29,8 @@ Use this method to get the text font filename of the selected keyboard.
The following script illustrate the use of
`getKeyboardTextFontFilename()`:
``` javascript
String textFontFilename = KMManager.getKeyboardTextFontFilename();
``` java
String textFontFilename = KMManager.getKeyboardTextFontFilename();
```
## See also