Skip to content

fix(android): differentiate between fontPath and fontUrl in Keyman Engine for Android#16188

Merged
ermshiperete merged 4 commits into
masterfrom
fix/android/16187_font
Jul 8, 2026
Merged

fix(android): differentiate between fontPath and fontUrl in Keyman Engine for Android#16188
ermshiperete merged 4 commits into
masterfrom
fix/android/16187_font

Conversation

@ermshiperete

@ermshiperete ermshiperete commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

PR #16146 introduced a problem with selecting a different font as display font so that we always ended up with not setting the font. This was caused by the font filenames now being a URL (which is necessary because they get processed by the web engine). However, the Android code checks for the existence of the font in order to create the typeface, which only works for local paths.

This PR modifies and simplifies KMKeyboard.getFontFilename to return the full path, renames KMKeyboard.txtFont and KMKeyboard.oskFont to make it clearer that they contain a path and not a URL.

Also initialize KMKeyboard.oskFontPath with empty string instead of null. This makes it consistent with txtFontPath and with the documented behavior of KMManager.getKeyboardOskFontFilename() (which returns KMKeyboard.oskFontPath).

Follows: #16146
Replaces: #16178
Fixes: #16187
Build-bot: release:android

User Testing

TEST_KEYBOARDHARNESS:

  • install the KeyboardHarness apk from this PR

  • Switch to the longpress '"|5% + keyboard

  • verify that the OSK uses the Code2001 font (compare with screenshot below)

  • type gbqJ and verify that the Code2001 font is used (compare with screenshot)

    image

TEST_KEYMAN:

  • install Keyman for Android apk
  • try different keyboards, verify that the OSK and displayed text look correct

TEST_FV:

  • install FirstVoices Keyboards for Android apk
  • try different keyboards, verify that the OSK and displayed text look correct

.

@keymanapp-test-bot

keymanapp-test-bot Bot commented Jul 2, 2026

Copy link
Copy Markdown

User Test Results

Test specification and instructions

  • 🟩 TEST_KEYBOARDHARNESS (SKIPPED): blocked because keyboardharness does not install
  • TEST_KEYMAN (PASSED) (notes)
  • TEST_FV (PASSED) (notes)

Test Artifacts

@keymanapp-test-bot keymanapp-test-bot Bot added this to the A19S32 milestone Jul 2, 2026
@keymanapp-test-bot keymanapp-test-bot Bot added has-user-test user-test-required User tests have not been completed and removed user-test-missing User tests have not yet been defined for the PR labels Jul 2, 2026
@ermshiperete
ermshiperete force-pushed the fix/android/16187_font branch 2 times, most recently from 92edddc to a26d081 Compare July 2, 2026 17:49
@ermshiperete
ermshiperete requested review from jahorton and mcdurdin July 2, 2026 17:49
PR #16146 introduced a problem with selecting a different font as
display font so that we always ended up with not setting the font.
This was caused by the font filenames now being a URL (which is necessary
because they get processed by the web engine). However, the Android code
checks for the existence of the font in order to create the typeface,
which only works for local paths.

This PR modifies and simplifies `KMKeyboard.getFontFilename` to return the
full path, renames `KMKeyboard.txtFont` and `KMKeyboard.oskFont` to make
it clearer that they contain a path and not a URL.

Also initialize `KMKeyboard.oskFontPath` with empty string instead of
`null`. This makes it consistent with` txtFontPath` and with the
documented behavior of `KMManager.getKeyboardOskFontFilename()` (which
returns `KMKeyboard.oskFontPath`).

Follows: #16146
Fixes: #16187
Build-bot: release:android
@ermshiperete
ermshiperete force-pushed the fix/android/16187_font branch from a26d081 to 1191e43 Compare July 2, 2026 17:53
@ermshiperete
ermshiperete marked this pull request as ready for review July 2, 2026 17:54
Comment thread android/docs/engine/KMManager/getFontTypeface.md Outdated
Comment thread android/docs/engine/KMManager/getKeyboardOskFontFilename.md Outdated
/**
* Returns the filename without path of the display font of the current keyboard.
*/
private String getKeyboardTextFontFilename() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we call this getKeyboardTextFontFilenameOnly() because of the legacy of KMManager.getKeyboardTextFontFilename() returning a full path?

Comment on lines +933 to +935
// REVIEW: Do we have to do anything if font is a JSONObject?
// See makeFontObj.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really understand this question

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In makeFontObj we have this questionable block of code that deals with a font as JSONObject. If that is still needed, then we'll likely have to do something here with a JSONObject as well.

Any suggestion for a better review or todo comment?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After some spelunking, came to the conclusion that the input parameter is always a filename, not a JSON object. So we can hopefully clean that up a bit.

Comment on lines +1069 to +1071
// 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);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's open an issue on this to follow up in the future once we have some logging. (Note that LogInfo doesn't go to Sentry though? Can we capture a Sentry event?)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LogInfo() calls Sentry.captureMessage, so I think it should end up on Sentry.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we concluded that this code path is not relevant, so we should clean up in a follow-up PR. We can run a set of user tests to verify that this doesn't break existing users.

Comment thread android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java
ermshiperete and others added 2 commits July 3, 2026 17:23
Co-authored-by: Marc Durdin <marc@durdin.net>
Co-authored-by: Eberhard Beilharz <ermshiperete@users.noreply.github.com>
@keyman-server keyman-server modified the milestones: A19S32, A19S33 Jul 4, 2026

@mcdurdin mcdurdin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, with some cleanup as a follow-up PR or two

Comment on lines +706 to +710
JSONObject jDisplayFont = makeFontObject(kFont, packageID);
JSONObject jOskFont = makeFontObject(kOskFont, packageID);

txtFont = getFontFilename(jDisplayFont);
oskFont = getFontFilename(jOskFont);
txtFontPath = getFontFilename(kFont, packageID);
oskFontPath = getFontFilename(kOskFont, packageID);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's try and make these font names consistent - we have font, displayfont, txtfont, oskfont, textfont -- but only two conceptual fonts -- TextFont and OskFont. For a follow-up cleanup PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +815 to +822
private String getPackageRootUrl(String packageID) {
if (packageID.equals(KMManager.KMDefault_UndefinedPackageID)) {
return getDataRootUrl() + KMManager.KMDefault_UndefinedPackageID + "/";
}
return getDataRootUrl() + KMManager.KMDefault_AssetPackages + "/" + packageID + "/";
}

private String getPackageRootPath(String packageID) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add comments on the difference between RootUrl and RootPath for future maintainers?

Comment on lines 928 to 931
if (FileUtils.hasFontExtension(font)) {
String fontRoot = KMManager.isDefaultFont(font) ? getDataRootPath() : getPackageRootPath(packageID);
return fontRoot + font;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we reverse this, so it becomes a validation precondition instead?

Suggested change
if (FileUtils.hasFontExtension(font)) {
String fontRoot = KMManager.isDefaultFont(font) ? getDataRootPath() : getPackageRootPath(packageID);
return fontRoot + font;
}
if (!FileUtils.hasFontExtension(font)) {
// QUESTION: do we log this?
return "";
}
String fontRoot = KMManager.isDefaultFont(font) ? getDataRootPath() : getPackageRootPath(packageID);
return fontRoot + font;

Comment on lines +933 to +935
// REVIEW: Do we have to do anything if font is a JSONObject?
// See makeFontObj.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After some spelunking, came to the conclusion that the input parameter is always a filename, not a JSON object. So we can hopefully clean that up a bit.

Comment on lines +1069 to +1071
// 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);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we concluded that this code path is not relevant, so we should clean up in a follow-up PR. We can run a set of user tests to verify that this doesn't break existing users.

@mcdurdin mcdurdin changed the title fix(android): fix setting font fix(android): differentiate between fontPath and fontUrl in Keyman Engine for Android Jul 8, 2026
@Meng-Heng Meng-Heng self-assigned this Jul 8, 2026
@Meng-Heng

Copy link
Copy Markdown
Contributor

Test Specs

  1. Keyman for Android 19.0.251-alpha-test-16188
  2. Android Studio Pixel 9 Android v15.0
  3. FirstVoice 19.0.251

Test Results

  • TEST_KEYMAN (PASSED):
  1. Launch Keyman without enabling anything in startup menu
  2. Go to Settings -> Install Keyboard or Dictionary -> Get:
    • Khmer Angkor
    • GFF Amharic
    • EuroLatin (SIL) default
    • Tamil Suratha Bamini
  3. Return to Home screen
  4. Verified: OSK, displayed text, typing, and applying predictive text work as expected, no error.
  5. Verified: After enabling everything in the startup menu work, OSK and typing does seem to work well.
  6. Verified: Compare keyboards to Keyman 18.0.249 doesn't seem to have any font dissimilarity.
  • TEST_FV (PASSED):
  1. Launch FirstVoice and select keyboards:
    • Dakelh
    • EuroLatin (SIL)
  2. Verified: OSK, font, and typing in Google Chrome work as expected.
  3. Verified: Character on the OSK and typed text seems to be normal size and in the center.

Co-authored-by: Marc Durdin <marc@durdin.net>
@ermshiperete

Copy link
Copy Markdown
Contributor Author

Follow-up-PR: #16211

ermshiperete added a commit that referenced this pull request Jul 8, 2026
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
@mcdurdin

mcdurdin commented Jul 8, 2026

Copy link
Copy Markdown
Member

TEST_KEYBOARDHARNESS SKIP blocked because keyboardharness does not install

@keymanapp-test-bot keymanapp-test-bot Bot removed the user-test-required User tests have not been completed label Jul 8, 2026
@ermshiperete
ermshiperete merged commit fcf8988 into master Jul 8, 2026
7 checks passed
@ermshiperete
ermshiperete deleted the fix/android/16187_font branch July 8, 2026 12:35
@github-project-automation github-project-automation Bot moved this from Todo to Done in Keyman Jul 8, 2026
ermshiperete added a commit that referenced this pull request Jul 8, 2026
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
@keyman-server

Copy link
Copy Markdown
Collaborator

Changes in this pull request will be available for download in Keyman version 19.0.252-alpha

ermshiperete added a commit that referenced this pull request Jul 14, 2026
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

bug(android): KeyboardHarness uses wrong font after #16146

4 participants