Merge branch 'feat/mac/expand-package' of https://github.com/keymanapp/keyman into feat/mac/expand-package
Some checks failed
Keyman Build Summary / Summarize build status checks (push) Has been cancelled

This commit is contained in:
Shawn Schantz 2026-07-21 14:53:54 -04:00
commit 777e3b3359
290 changed files with 3504 additions and 1673 deletions

View file

@ -137,7 +137,7 @@ jobs:
strategy:
fail-fast: true
matrix:
dist: [jammy, noble, questing]
dist: [jammy, noble, resolute]
steps:
- name: Checkout

4
.gitignore vendored
View file

@ -186,3 +186,7 @@ lcov.info
# flag file for build script
.configured
# see common/tools/api-extractor/README.md
tsdoc.json
api-extractor.json

View file

@ -1,5 +1,49 @@
# Keyman Version History
## 19.0.258 alpha 2026-07-15
* chore(android): remove doc for non-existing deprecated functions (#16244)
## 19.0.257 alpha 2026-07-14
* chore(linux): remove questing, add stonking (#16236)
* chore(android): cleanup Android build scripts and artifact filenames (#16234)
* chore(web): declare scope for stubAndKeyboardCache members (#16221)
* fix(android): improve clarity of keyboard script error popup (#16228)
* chore(android): log legacy cloud keyboards (#16237)
* chore(android): cleanup font variable names, remove obsolete code (#16211)
## 19.0.256 alpha 2026-07-13
* fix(android): add permissions for Sentry and set default keyboard (#16216)
* maint(resources): add extra debug reporting for builder (#16230)
## 19.0.255 alpha 2026-07-11
* maint(android): use KEYMAN_TIER instead of TIER.md (#16219)
* feat(ios): add keyman-version to package-version check (#16138)
* docs(web): cleanup of index pages and page titles (#16171)
* feat(android): add keyman-version to package-version check (#16139)
## 19.0.254 alpha 2026-07-10
* fix(android): handle blank keyboard tap properly (#16217)
* fix(web): define IME interfaces in `KeyboardInterfaceBase` (#16170)
## 19.0.253 alpha 2026-07-09
* fix(web): resync model in `set osk()` (as used by Server) (#16127)
* chore(developer): consolidate api-extractor usage in Developer (#16135)
## 19.0.252 alpha 2026-07-08
* chore(linux): reorder buttons on kmp install window (#16210)
* refactor(web): rename `keymanweb` to `keyman` in UI modules (#16174)
* chore: remove deprecated "X-UA-Compatible" and "apple-mobile-web-app-capable" metas (#16176)
* fix(android): refactor `KMLog` - simplification and added resilience (#16152)
* fix(android): differentiate between fontPath and fontUrl in Keyman Engine for Android (#16188)
* fix(web): fixes cookie unit-tests and path for OSK resources during testing (#16209)
## 19.0.251 alpha 2026-07-02
* fix(android): skip test on Windows that triggers androidx WebKit bug (#16184)

View file

@ -1 +1 @@
19.0.252
19.0.259

View file

@ -1,5 +1,6 @@
#!/usr/bin/env bash
# Build Keyman for Android app (KMAPro)
# Keyman is copyright (C) SIL Global. MIT License.
#
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
@ -9,111 +10,92 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "$KEYMAN_ROOT/resources/build/utils.inc.sh"
. "$KEYMAN_ROOT/resources/build/build-help.inc.sh"
. "$KEYMAN_ROOT/resources/build/build-download-resources.sh"
. "$KEYMAN_ROOT/android/KMAPro/build-play-store-notes.inc.sh"
# ################################ Main script ################################
# Definition of global compile constants
CONFIG="release"
BUILD_FLAGS="build -x lint -x test" # Gradle build w/o test
TEST_FLAGS="-x assembleRelease lintRelease testRelease" # Gradle test w/o build
DAEMON_FLAG=
builder_describe "Builds Keyman for Android app." \
"@/android/KMEA" \
"clean" \
"configure" \
"build" \
"test Runs lint and unit tests." \
"publish-symbols Publishes symbols to Sentry." \
"test Runs lint and unit tests." \
"publish-symbols Publishes symbols to Sentry." \
"publish-play-store Publishes the APK to the Play Store."
# parse before describe_outputs to check debug flags
builder_parse "$@"
if builder_is_debug_build; then
builder_heading "### Debug config ####"
CONFIG="debug"
BUILD_FLAGS="assembleDebug -x lintDebug -x testDebug"
TEST_FLAGS="-x assembleDebug lintDebug testDebug"
else
BUILD_FLAGS="build -x lint -x test" # Gradle build w/o test
TEST_FLAGS="-x assembleRelease lintRelease testRelease" # Gradle test w/o build
fi
if builder_is_ci_build; then
GRADLE_DAEMON=--no-daemon
else
GRADLE_DAEMON=
fi
# Locations for bundled keyboard and lexical model
KEYBOARD_PACKAGE_ID="sil_euro_latin"
KEYBOARDS_TARGET="android/KMAPro/kMAPro/src/main/assets/${KEYBOARD_PACKAGE_ID}.kmp"
MODEL_PACKAGE_ID="nrc.en.mtnt"
MODELS_TARGET="android/KMAPro/kMAPro/src/main/assets/${MODEL_PACKAGE_ID}.model.kmp"
builder_describe_outputs \
configure "/${MODELS_TARGET}" \
build /android/KMAPro/kMAPro/build/outputs/apk/$CONFIG/keyman-${KEYMAN_VERSION_FOR_FILENAME}.apk
#### Build
# Parse args
if builder_is_ci_build; then
DAEMON_FLAG=--no-daemon
fi
build /android/KMAPro/kMAPro/build/outputs/apk/$BUILDER_CONFIGURATION/keyman-${KEYMAN_VERSION_FOR_FILENAME}.apk
#### Build action definitions ####
# Check about cleaning artifact paths
if builder_start_action clean; then
rm -rf "$KEYMAN_ROOT/android/KMAPro/kMAPro/build/outputs"
builder_finish_action success clean
fi
if builder_start_action configure; then
do_configure() {
downloadKeyboardPackage "$KEYBOARD_PACKAGE_ID" "${KEYMAN_ROOT}/$KEYBOARDS_TARGET"
downloadModelPackage "$MODEL_PACKAGE_ID" "${KEYMAN_ROOT}/$MODELS_TARGET"
}
builder_finish_action success configure
fi
if builder_start_action build; then
do_build() {
# Copy Keyman Engine for Android
cp "${KEYMAN_ROOT}/android/KMEA/app/build/outputs/aar/keyman-engine-${CONFIG}.aar" "${KEYMAN_ROOT}/android/KMAPro/kMAPro/libs/keyman-engine.aar"
cp "${KEYMAN_ROOT}/android/KMEA/app/build/outputs/aar/keyman-engine-${BUILDER_CONFIGURATION}.aar" "${KEYMAN_ROOT}/android/KMAPro/kMAPro/libs/keyman-engine.aar"
# Convert markdown to html for offline help
build_help_html android KMAPro/kMAPro/src/main/assets/info
echo "BUILD_FLAGS $BUILD_FLAGS"
./gradlew $DAEMON_FLAG clean $BUILD_FLAGS
builder_echo "BUILD_FLAGS $BUILD_FLAGS"
./gradlew $GRADLE_DAEMON $BUILD_FLAGS
mv "${KEYMAN_ROOT}/android/KMAPro/kMAPro/build/outputs/apk/$CONFIG/keyman-${KEYMAN_VERSION}.apk" "${KEYMAN_ROOT}/android/KMAPro/kMAPro/build/outputs/apk/$CONFIG/keyman-${KEYMAN_VERSION_FOR_FILENAME}.apk"
mv "${KEYMAN_ROOT}/android/KMAPro/kMAPro/build/outputs/apk/$BUILDER_CONFIGURATION/keyman-${KEYMAN_VERSION}.apk" "${KEYMAN_ROOT}/android/KMAPro/kMAPro/build/outputs/apk/$BUILDER_CONFIGURATION/keyman-${KEYMAN_VERSION_FOR_FILENAME}.apk"
}
builder_finish_action success build
fi
do_test() {
builder_echo "TEST_FLAGS $TEST_FLAGS"
./gradlew $GRADLE_DAEMON $TEST_FLAGS
}
if builder_start_action test; then
echo "TEST_FLAGS $TEST_FLAGS"
./gradlew $DAEMON_FLAG $TEST_FLAGS
builder_finish_action success test
fi
publish_symbols() {
do_publish_symbols() {
if builder_is_ci_build && builder_is_ci_build_level_release; then
# TODO: what does publishSentry even do?
./gradlew $DAEMON_FLAG publishSentry
./gradlew $GRADLE_DAEMON publishSentry
builder_echo "Making a Sentry release for tag $KEYMAN_VERSION_GIT_TAG"
sentry-cli upload-dif -p keyman-android --include-sources
sentry-cli releases -p keyman-android files $KEYMAN_VERSION_GIT_TAG upload-sourcemaps ./
fi
}
publish_play_store() {
do_publish_play_store() {
if builder_is_ci_build && builder_is_ci_build_level_release; then
generateReleaseNotes
# Publish Keyman for Android to Play Store
./gradlew $DAEMON_FLAG publishReleaseApk
./gradlew $GRADLE_DAEMON publishReleaseApk
fi
}
builder_run_action publish-symbols publish_symbols
builder_run_action publish-play-store publish_play_store
builder_run_action clean rm -rf "$KEYMAN_ROOT/android/KMAPro/kMAPro/build"
builder_run_action configure do_configure
builder_run_action build do_build
builder_run_action test do_test
builder_run_action publish-symbols do_publish_symbols
builder_run_action publish-play-store do_publish_play_store

View file

@ -8,8 +8,7 @@ plugins {
ext.rootPath = '../../'
apply from: "$rootPath/version.gradle"
String tier = new File('../../TIER.md').getText('UTF-8').trim();
String tier = System.getenv("KEYMAN_TIER");
java {
toolchain {

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

@ -8,7 +8,7 @@ ext.rootPath = '../../'
apply from: "$rootPath/version.gradle"
base {
// Specify library filename keyman-engine-$CONFIG.aar
// Specify library filename keyman-engine-$BUILDER_CONFIGURATION.aar
archivesName = "keyman-engine"
}

View file

@ -22,10 +22,6 @@ sentryManager.init();
window.addEventListener('load', init, false);
function loadDefaultKeyboard() {
notifyHost('reloadAfterError');
}
function init() {
//document.body.style.backgroundColor="transparent";
//window.console.log('Device type = '+device);
@ -82,7 +78,19 @@ function init() {
keyman.addEventListener('keyboardchange', setIsChiral);
keyman.core.languageProcessor.on('statechange', onStateChange);
document.body.addEventListener('touchend', loadDefaultKeyboard);
// If the keyboard fails to display, this usually indicates that a Javascript
// error has occurred, perhaps in the active keyboard. So we have a last-gasp
// fallback in that situation; if the user touches the blank document, we will
// attempt to reload the default keyboard.
document.addEventListener('touchend', (event) => {
if(event.target == document.documentElement) {
// The error will be reported through to Sentry if the user has error
// reporting enabled; breadcrumbs can give us helpful details on possible
// root causes for the error
window.console.error('user touched the document body, which can only happen if the keyboard was not presented. This is usually caused by another error.');
notifyHost('reloadAfterError');
}
});
notifyHost('pageLoaded');
}

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 textFontPath = "";
private static String oskFontPath = "";
private final String fontUndefined = "undefined";
private GestureDetector gestureDetector;
private static ArrayList<OnKeyboardEventListener> kbEventListeners = null;
@ -312,7 +310,7 @@ final class KMKeyboard extends WebView {
// This duplicates the sendKMWError message, which itself duplicates the reporting now
// managed by sentry-manager on the js side in patch in #6890. It does not give us
// additional useful information. So we don't re-send to Sentry.
sendError(packageID, keyboardID, "", false);
sendError(packageID, keyboardID, "", null);
}
return true;
@ -551,7 +549,7 @@ final class KMKeyboard extends WebView {
* @return String
*/
public static String textFontFilename() {
return txtFont;
return textFontPath;
}
/**
@ -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
@ -603,7 +601,11 @@ final class KMKeyboard extends WebView {
}
if (!KMManager.shouldAllowSetKeyboard() || kbInfo == null) {
sendError(packageID, keyboardID, languageID, true);
if(!KMManager.shouldAllowSetKeyboard()) {
sendError(packageID, keyboardID, languageID, "setKeyboard.short failed with shouldAllowSetKeyboard == false");
} else {
sendError(packageID, keyboardID, languageID, "setKeyboard.short failed with kbInfo == null");
}
kbInfo = KeyboardController.getInstance().getKeyboardInfo(0);
retVal = false;
} else {
@ -634,7 +636,7 @@ final class KMKeyboard extends WebView {
if (!KMManager.shouldAllowSetKeyboard() ||
(packageID.equals(KMManager.KMDefault_UndefinedPackageID) && keyboardVersion == null)) {
sendError(packageID, keyboardID, languageID, true);
sendError(packageID, keyboardID, languageID, "prepareKeyboardSwitch");
Keyboard kbInfo = KeyboardController.getInstance().getKeyboardInfo(0);
packageID = kbInfo.getPackageID();
keyboardID = kbInfo.getKeyboardID();
@ -648,8 +650,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.
@ -664,15 +664,15 @@ final class KMKeyboard extends WebView {
}
public boolean setKeyboard(String packageID, String keyboardID, String languageID,
String keyboardName, String languageName, String kFont,
String kOskFont) {
String keyboardName, String languageName, String textFontFilename,
String oskFontFilename) {
return setKeyboard(packageID, keyboardID, languageID, keyboardName, languageName,
kFont, kOskFont, null);
textFontFilename, oskFontFilename, 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 textFontFilename,
String oskFontFilename, String displayName) {
if (packageID == null || keyboardID == null || languageID == null || keyboardName == null || languageName == null) {
return false;
}
@ -688,15 +688,19 @@ final class KMKeyboard extends WebView {
if (!KMManager.shouldAllowSetKeyboard() ||
(packageID.equals(KMManager.KMDefault_UndefinedPackageID) && keyboardVersion == null)) {
sendError(packageID, keyboardID, languageID, true);
if(!KMManager.shouldAllowSetKeyboard()) {
sendError(packageID, keyboardID, languageID, "setKeyboard.full failed with shouldAllowSetKeyboard == false");
} else {
sendError(packageID, keyboardID, languageID, "setKeyboard.full failed with packageID == KMDefault_UndefinedPackageID and keyboardVersion == null");
}
Keyboard kbInfo = KeyboardController.getInstance().getKeyboardInfo(0);
packageID = kbInfo.getPackageID();
keyboardID = kbInfo.getKeyboardID();
languageID = kbInfo.getLanguageID();
keyboardName = kbInfo.getKeyboardName();
languageName = kbInfo.getLanguageName();
kFont = kbInfo.getFont();
kOskFont = kbInfo.getOSKFont();
textFontFilename = kbInfo.getFont();
oskFontFilename = kbInfo.getOSKFont();
retVal = false;
// Keyboard changed, so determine version again
@ -704,16 +708,14 @@ final class KMKeyboard extends WebView {
KMManager.getLatestKeyboardFileVersion(getContext(), packageID, keyboardID) : null;
}
setPackageRoot(packageID);
if(oskFontFilename == null || oskFontFilename.isEmpty())
oskFontFilename = textFontFilename;
if(kOskFont == null || kOskFont.isEmpty())
kOskFont = kFont;
JSONObject textFont = makeFontObject(textFontFilename, packageID);
JSONObject oskFont = makeFontObject(oskFontFilename, packageID);
JSONObject jDisplayFont = makeFontObject(kFont);
JSONObject jOskFont = makeFontObject(kOskFont);
txtFont = getFontFilename(jDisplayFont);
oskFont = getFontFilename(jOskFont);
textFontPath = getFontFilename(textFontFilename, packageID);
oskFontPath = getFontFilename(oskFontFilename, packageID);
String kbKey = KMString.format("%s_%s", languageID, keyboardID);
@ -728,8 +730,8 @@ final class KMKeyboard extends WebView {
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);
@ -786,50 +788,62 @@ final class KMKeyboard extends WebView {
}
public boolean getChirality() {
return this.isChiral;
}
// Display localized Toast notification that keyboard selection failed, so loading default keyboard.
// Also sends a message to Sentry (not localized)
private void sendError(String packageID, String keyboardID, String languageID, boolean reportToSentry) {
/**
* Display localized Toast notification that keyboard selection failed, so
* loading default keyboard. Also sends a message to Sentry (not localized)
*/
private void sendError(String packageID, String keyboardID, String languageID, String sentryMessage) {
this.currentKeyboardErrorReports++;
if(this.currentKeyboardErrorReports == 1) {
BaseActivity.makeToast(context, R.string.fatal_keyboard_error_short, Toast.LENGTH_LONG, packageID, keyboardID, languageID);
BaseActivity.makeToast(context, R.string.fatal_keyboard_error, Toast.LENGTH_LONG, packageID, keyboardID, languageID);
}
if(this.currentKeyboardErrorReports < 5 && DependencyUtil.libraryExists(LibraryType.SENTRY) && Sentry.isEnabled() && reportToSentry) {
// We'll only report up to 5 errors in a given keyboard to avoid spamming
// errors and using unnecessary bandwidth doing so
// Don't use localized string R.string.fatal_keyboard_error msg for Sentry
String msg = KMString.format("Error in keyboard %1$s:%2$s for %3$s language.",
packageID, keyboardID, languageID);
if(this.currentKeyboardErrorReports < 5 && sentryMessage != null && DependencyUtil.libraryExists(LibraryType.SENTRY) && Sentry.isEnabled()) {
// We'll only report up to 5 errors in a given keyboard to avoid spamming errors and using unnecessary bandwidth doing so
String msg = KMString.format("Script error in keyboard webview\nContext: %4$s\nActive package: %1$s\nActive keyboard: %2$s\nActive language: %3$s",
packageID, keyboardID, languageID, sentryMessage);
Sentry.captureMessage(msg);
}
}
// 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 +931,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 fontFilename String - Font filename
* @param packageID String - Package ID
* @return String - Full path to the font file. If fontFilename 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 fontFilename, String packageID) {
if(fontFilename == null || fontFilename.equals("")) {
return "";
}
return font;
if (!FileUtils.hasFontExtension(fontFilename)) {
// QUESTION: do we log this?
return "";
}
String fontRoot = KMManager.isDefaultFont(fontFilename) ? getDataRootPath() : getPackageRootPath(packageID);
return fontRoot + fontFilename;
}
@SuppressLint("InflateParams")
@ -1061,12 +1056,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 +1074,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 +1095,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 +1104,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

@ -45,7 +45,7 @@ public class CloudRepository {
public static final String API_STAGING_HOST = "api.keyman.com"; // #7227 disabling: "api.keyman-staging.com";
public static final String API_MODEL_LANGUAGE_FORMATSTR = "https://%s/model?q=bcp47:%s";
public static final String API_PACKAGE_VERSION_FORMATSTR = "https://%s/package-version?platform=android%s%s";
public static final String API_PACKAGE_VERSION_FORMATSTR = "https://%s/package-version?platform=android&keyman-version=%s%s%s";
private Dataset memCachedDataset;
private Calendar lastLoad; // To be used for Dataset caching.
@ -205,7 +205,7 @@ public class CloudRepository {
}
}
String queryURL = String.format(API_PACKAGE_VERSION_FORMATSTR, getHost(), keyboardQuery, lexicalModelQuery);
String queryURL = String.format(API_PACKAGE_VERSION_FORMATSTR, getHost(), KMManager.getVersion(), keyboardQuery, lexicalModelQuery);
return new CloudApiTypes.CloudApiParam(
CloudApiTypes.ApiTarget.PackageVersion, queryURL).setType(CloudApiTypes.JSONType.Object);
}

View file

@ -49,6 +49,7 @@ public class Keyboard extends LanguageResource implements Serializable {
*/
public Keyboard(JSONObject installedObj) {
this.fromJSON(installedObj);
logIfLegacyKeyboard();
}
/**
@ -79,6 +80,8 @@ public class Keyboard extends LanguageResource implements Serializable {
this.helpLink = keyboardJSON.optString(KMManager.KMKey_CustomHelpLink,
KMString.format(HELP_URL_FORMATSTR, HELP_URL_HOST, this.resourceID, this.version));
logIfLegacyKeyboard();
} catch (JSONException e) {
KMLog.LogException(TAG, "Keyboard exception parsing JSON: ", e);
}
@ -96,6 +99,8 @@ public class Keyboard extends LanguageResource implements Serializable {
this.isNewKeyboard = isNewKeyboard;
this.font = (font != null) ? font : "";
this.oskFont = (oskFont != null) ? oskFont : "";
logIfLegacyKeyboard();
}
public Keyboard(Keyboard k) {
@ -108,6 +113,12 @@ public class Keyboard extends LanguageResource implements Serializable {
this.displayName = k.getDisplayName();
}
private void logIfLegacyKeyboard() {
if (this.packageID.equals(KMManager.KMDefault_UndefinedPackageID)) {
KMLog.LogInfo(TAG, "Constructing legacy keyboard: " + this.resourceID);
}
}
public String getKeyboardID() { return getResourceID(); }
public String getKeyboardName() { return getResourceName(); }

View file

@ -24,16 +24,11 @@ import io.sentry.SentryLevel;
public final class KMLog {
private static final String TAG = "KMLog";
private static final String KEYBOARD_TAG = "keyboardId";
private static final String KEYBOARD_COUNT_TAG = "installedKeyboardCount";
private static final String MODEL_TAG = "modelId";
private static final String LANGCODE_TAG = "languageCode";
// Some of the methods used to generate debug logging information can, themselves,
// trigger errors that can also trigger the same logging. We must not get
// caught in an infinite loop / stack-overflow; this field helps us avoid states
// that would otherwise cause error-looping, etc.
private static boolean isLogging = false;
private static final String KEYBOARD_TAG = "keyman.keyboardId";
private static final String KEYBOARD_COUNT_TAG = "keyman.installedKeyboardCount";
private static final String MODEL_TAG = "keyman.modelId";
private static final String LANGCODE_TAG = "keyman.languageCode";
private static final String DEBUG_LOGGING_ERROR_TAG = "keyman.debugLoggingError";
private static void tagDebugInfo() {
String kbdId = "";
@ -57,13 +52,16 @@ public final class KMLog {
}
}
} catch (Exception ex) {
String msg = ex.getMessage() == null ? "" : ex.getMessage();
Sentry.setExtra("debugLoggingError", msg);
Sentry.setTag(DEBUG_LOGGING_ERROR_TAG, ex.getMessage() == null ? "" : ex.getMessage());
}
Sentry.setExtra(KEYBOARD_TAG, kbdId);
Sentry.setExtra(KEYBOARD_COUNT_TAG, "" + kbdCount);
Sentry.setExtra(LANGCODE_TAG, lngCode);
Sentry.setExtra(MODEL_TAG, modelId);
Sentry.setTag(KEYBOARD_TAG, kbdId);
Sentry.setTag(KEYBOARD_COUNT_TAG, "" + kbdCount);
Sentry.setTag(LANGCODE_TAG, lngCode);
Sentry.setTag(MODEL_TAG, modelId);
}
private static boolean canLogToSentry() {
return DependencyUtil.libraryExists(LibraryType.SENTRY) && Sentry.isEnabled();
}
/**
@ -72,19 +70,20 @@ public final class KMLog {
* @param msg String of the info message
*/
public static void LogInfo(String tag, String msg) {
if(isLogging) {
if (msg == null || msg.isEmpty()) {
return;
}
isLogging = true;
if (msg != null && !msg.isEmpty()) {
Log.i(tag, msg);
if (DependencyUtil.libraryExists(LibraryType.SENTRY) && Sentry.isEnabled()) {
tagDebugInfo();
Sentry.captureMessage(msg, SentryLevel.INFO);
}
Log.i(tag, msg);
if (!canLogToSentry()) {
return;
}
isLogging = false;
Sentry.withScope(scope -> {
tagDebugInfo();
Sentry.captureMessage(msg, SentryLevel.INFO);
});
}
/**
@ -98,15 +97,9 @@ public final class KMLog {
return;
}
if(isLogging) {
return;
}
isLogging = true;
Log.i(tag, msg);
if (!DependencyUtil.libraryExists(LibraryType.SENTRY) || !Sentry.isEnabled()) {
isLogging = false;
if (!canLogToSentry()) {
return;
}
@ -114,27 +107,30 @@ public final class KMLog {
crumb.setMessage(msg);
crumb.setLevel(SentryLevel.INFO);
if(addStackTrace) {
StackTraceElement[] rawTrace = Thread.currentThread().getStackTrace();
try {
if(addStackTrace) {
StackTraceElement[] rawTrace = Thread.currentThread().getStackTrace();
// The call that gets us the stack-trace above... shows up in the
// stack trace, so we'll skip the first few (redundant) entries.
int skipCount = 3;
// The call that gets us the stack-trace above... shows up in the
// stack trace, so we'll skip the first few (redundant) entries.
int skipCount = 3;
// Sentry does limit the size of messages... so let's just
// keep 10 entries and call it a day.
int limit = Math.min(rawTrace.length, 10 + skipCount);
if(rawTrace.length > skipCount) {
String[] trace = new String[limit - skipCount];
for (int i = skipCount; i < limit; i++) {
trace[i-skipCount] = rawTrace[i].toString();
// Sentry does limit the size of messages... so let's just
// keep 10 entries and call it a day.
if(rawTrace.length > skipCount) {
int limit = Math.min(rawTrace.length, 10 + skipCount);
String[] trace = new String[limit - skipCount];
for (int i = skipCount; i < limit; i++) {
trace[i-skipCount] = rawTrace[i].toString();
}
crumb.setData("stacktrace", trace);
}
crumb.setData("stacktrace", trace);
}
} catch (Exception e) {
Sentry.captureException(e);
}
tagDebugInfo();
Sentry.addBreadcrumb(crumb);
isLogging = false;
}
/**
@ -143,23 +139,32 @@ public final class KMLog {
* @param msg String of the error message
*/
public static void LogError(String tag, String msg) {
if(isLogging) {
if (msg == null || msg.isEmpty()) {
return;
}
isLogging = true;
if (msg != null && !msg.isEmpty()) {
Log.e(tag, msg);
Log.e(tag, msg);
try {
// On alpha and beta tiers, we pop an error toast so testers can be aware
// of the error
if (KMManager.getTier(BuildConfig.KEYMAN_ENGINE_VERSION_NAME) != KMManager.Tier.STABLE) {
BaseActivity.makeToast(null, msg, Toast.LENGTH_LONG);
}
if (DependencyUtil.libraryExists(LibraryType.SENTRY) && Sentry.isEnabled()) {
tagDebugInfo();
Sentry.captureMessage(msg, SentryLevel.ERROR);
} catch(Exception e) {
if(canLogToSentry()) {
Sentry.captureException(e);
}
}
isLogging = false;
if (!canLogToSentry()) {
return;
}
Sentry.withScope(scope -> {
tagDebugInfo();
Sentry.captureMessage(msg, SentryLevel.ERROR);
});
}
/**
@ -169,28 +174,7 @@ public final class KMLog {
* @param e Throwable exception
*/
public static void LogException(String tag, String msg, Throwable e) {
if(isLogging) {
return;
}
isLogging = true;
String errorMsg = "";
if (msg != null && !msg.isEmpty()) {
errorMsg = msg + "\n" + e;
} else if (e != null) {
errorMsg = e.getMessage();
}
Log.e(tag, errorMsg, e);
if (KMManager.getTier(BuildConfig.KEYMAN_ENGINE_VERSION_NAME) != KMManager.Tier.STABLE) {
BaseActivity.makeToast(null, errorMsg, Toast.LENGTH_LONG);
}
if (DependencyUtil.libraryExists(LibraryType.SENTRY) && Sentry.isEnabled()) {
tagDebugInfo();
Sentry.addBreadcrumb(errorMsg);
Sentry.captureException(e);
}
isLogging = false;
KMLog.LogExceptionWithData(tag, msg, null, null, e);
}
/**
@ -203,25 +187,48 @@ public final class KMLog {
*/
public static void LogExceptionWithData(String tag, String msg,
String objName, Object obj, Throwable e) {
if(isLogging) {
String errorMsg = "";
try {
if (msg != null && !msg.isEmpty()) {
errorMsg = msg + "\n" + e;
} else if (e != null) {
errorMsg = e.toString();
}
// On alpha and beta tiers, we pop an error toast so testers can be aware
// of the exception
if (KMManager.getTier(BuildConfig.KEYMAN_ENGINE_VERSION_NAME) != KMManager.Tier.STABLE) {
BaseActivity.makeToast(null, errorMsg, Toast.LENGTH_LONG);
}
} catch (Exception innerE) {
if (canLogToSentry()) {
Sentry.captureException(innerE);
}
}
Log.e(tag, errorMsg);
if (!canLogToSentry()) {
return;
}
isLogging = true;
if (obj != null && DependencyUtil.libraryExists(LibraryType.SENTRY) && Sentry.isEnabled()) {
Sentry.addBreadcrumb(errorMsg);
Sentry.withScope(scope -> {
tagDebugInfo();
String objStr = null;
try {
objStr = obj.toString();
Sentry.setExtra(objName, objStr);
} catch (Exception innerE) {
LogException(TAG, "Sentry.setExtra failed for " + objName, innerE);
if(obj != null && objName != null) {
Sentry.setExtra(objName, obj.toString());
}
} catch(Exception innerE) {
Sentry.captureException(innerE);
}
// Report the original exception
LogException(tag, msg, e);
// And remove the exception-specific tagged data, lest it also be
// tracked on subsequent errors not associated with the current call.
Sentry.removeExtra(objName);
}
isLogging = false;
Sentry.captureException(e);
if(obj != null && objName != null) {
// And remove the exception-specific tagged data, lest it also be
// tracked on subsequent errors not associated with the current call.
Sentry.removeExtra(objName);
}
});
}
}

View file

@ -80,8 +80,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file">የእገዛ ፋይልን ለማየት ፋይል ፕሮቫይደሩ ቤተ መጽሐፍት ያስፈልገዋል %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard">የገበታ ሰሌዳ ፋታል ችግር ዝርዝር %1$s:%2$s ለ %3$s ቋንቋ፤ መደበኛ ቋንቋ ጭኗል።</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language).">የፊደል ገበታ ሰሌዳው ችግር %1$s:%2$s ለ %3$s ቋንቋ።</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">ተያያዥነት ያለውን መዝገበ ቃላት ለመጫን እየፈተሸ ነው።</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download">የሚወርደውን መዝገበ ቃላት ከኪይማን ሰርቨር ጋር መገናኘት አልተቻለም</string>

View file

@ -88,8 +88,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file">مكتبة FileProvider مطلوبة لعرض ملفات المساعدة: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard">خطأ كبير في لوحة مفاتيح مع %1$s:%2$s لأجل لغة %3$s. سيتم تحميل لوحة المفاتيح الأساسية.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language).">خطأ في لوحة مفاتيح %1$s:%2$s لأجل لغة %3$s.</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">يجري التحقق من قاموس ذي صلة للتحميل</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> لا يمكن الاتصال بخادم Keyman للتحقق من تنزيل القاموس المرتبط</string>

View file

@ -80,8 +80,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file">FileProvider (Fayl Təminatçısı) kitabxanasına kömək sənədinə baxmaq lazımdır:%1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard">%3$s dili üçün %1$s ilə ölümcül klaviatura xətası :%3$s dili üçün %2$s. Varsayılan klaviatura yüklənir.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language).">%3$s dil üçün %1$s:%2$s klaviaturasında xəta.</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Yükləmək üçün əlaqəli lüğət yoxlanılır</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> Cannot connect to Keyman server to check for associated dictionary to download</string>

View file

@ -80,8 +80,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file">Ἀπαιτεῖται βιβλιοθήκη FileProvider γιὰ νὰ δεῖτε ἀρχεῖο βοηθείας: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard">Μοιραῖο σφάλμα πληκτρολογίου στὸ %1$s:%2$s γιὰ τὴν %3$s γλῶσσα. Φορτώνεται προεπιλεγμένο πληκτρολόγιο.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language).">Σφάλμα στὸ πληκτρολόγιο %1$s:%2$s γιὰ τὴν γλῶσσα \"%3$s\".</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Ἀναζήτηση σχετικοῦ λεξικοῦ πρὸς μεταφόρτωσιν</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download">Ἀδυναμία συνδέσεως μὲ τὸν διακομιστὴ Keyman γιὰ τὴν εὕρεση σχετικοῦ λεξικοῦ πρὸς μεταφόρτωσιν</string>

View file

@ -80,8 +80,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file"> Se necesita la librería FileProvider para ver el archivo de ayuda: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> Error fatal del teclado con %1$s:%2$s para el idioma %3$s. Cargando teclado predeterminado.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> Error en el teclado %1$s:%2$s para el idioma %3$s.</string>
<!-- Context: Alert Dialog body: System WebView not installed -->
<string name="body_install_webview" comment="Notification when WebView not installed"> Keyman requiere que WebView esté instalado.\"</string>
<!-- Context: Alert Dialog body: System WebView installed but not enabled -->

View file

@ -84,8 +84,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file">FilProvider library bindawar ley fakkakanal shu\'ul da: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard">Mushkila kabirey bey %1$s:%2$s layl %3$s lugga. Dassasan shabal keyboard hanal gabul.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language).">Mushkila fyl keyboard %1$s:%2$s layl %3$s lugga.</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Shafiyan kakkad abu kalimat shaba da ley dassasana</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> Cannot connect to Keyman server to check for associated dictionary to download</string>

View file

@ -80,8 +80,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file"> Mda akwa bara library na ar FileProvider kamnyar ka mdǝ wutǝ fayil ar dlara: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> Dametǝdzi na ar Fatal akwa keyboard ka %1$s:%2$s ala %3$s hulfur mnya. Akwa hara lodi ar default keyboard.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> Dametǝdzi akwa keyboard %1$s:%2$s na ar %3$s hulfur mnya.</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Akwa pakǝ dictionary na kǝɓa kari kamnyar katsa hara dawunlodi ni</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> Cannot connect to Keyman server to check for associated dictionary to download</string>

View file

@ -80,8 +80,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file"> Gada bara library nǝr FileProvider akuyaka gǝ wul su akwa fail nǝr dlarɛ: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> Fatal keyboard error with %1$s:%2$s for %3$s language. Loading default keyboard.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> Su kura kal kalakwa kibod %1$s:%2$s nǝr %3$s nya.</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Ata kǝra chakǝnta akuyaka ɓui suyere tara tsukwar bwar dictionary kadǝbar ka ndǝ download ko sǝgǝl akǝr</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> Gadiyata mǝzi bui sava nǝr Keyman wɛ akuya ka gǝ bui ka sǝgǝl aka jiliyir dikshinari namtǝ gada bara. </string>

View file

@ -84,8 +84,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file"> Knihovna FileProvider potřebná k zobrazení nápovědy: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> Fatální chyba klávesnice s %1$s:%2$s pro jazyk %3$s . Načítání výchozí klávesnice.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> Chyba v klávesnici %1$s:%2$s pro jazyk %3$s.</string>
<!-- Context: Alert Dialog body: System WebView not installed -->
<string name="body_install_webview" comment="Notification when WebView not installed">Keyman vyžaduje, aby byl nainstalován WebView.“</string>
<!-- Context: Alert Dialog body: System WebView installed but not enabled -->

View file

@ -80,8 +80,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file"> FileProvider-Bibliothek zum Anzeigen der Hilfe-Datei: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> Fataler Tastaturfehler mit %1$s:%2$s für %3$s Sprache. Standard-Tastatur wird geladen.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> Fehler in Tastatur %1$s:%2$s für %3$s Sprache.</string>
<!-- Context: Alert Dialog body: System WebView not installed -->
<string name="body_install_webview" comment="Notification when WebView not installed"> Keyman benötigt die Installation von WebView.\"</string>
<!-- Context: Alert Dialog body: System WebView installed but not enabled -->

View file

@ -80,8 +80,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file"> Biblioteca de FileProvider necesaria para ver el archivo de ayuda: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> Error de teclado fatal con %1$s:%2$s para %3$s idioma. Cargando teclado predeterminado.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> Error en el teclado %1$s:%2$s para %3$s idioma.</string>
<!-- Context: Alert Dialog body: System WebView not installed -->
<string name="body_install_webview" comment="Notification when WebView not installed"> Keyman requiere que WebView esté instalado.\"</string>
<!-- Context: Alert Dialog body: System WebView installed but not enabled -->

View file

@ -80,8 +80,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file"> Babal fayel ɗo mari haaje laarugo fayel mballa: %1$s </string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> Kibod man wari bee masala bee %1$s:%2$s jee %3$s ɗemngal. Ɗo nastina kibod ko naftirta. </string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> Masala nder kibod %1$s:%2$s jee %3$s ɗemngal. </string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Ɗo tefa deftere ma`ana kalimaji ngam jippina</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> Waawaa seŋaade e sarworde Keyman ngam ƴeewtaade so saggitorde yahdoonde heen ena woodi</string>

View file

@ -80,8 +80,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file">Deftordu FileProvider ena soklaa ngam yiyde fiilde ballal: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard">Juumre tappirde halkoore e %1$s: %2$s mo ɗemngal %3$s. Nana loowa tappirde woowaande.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> Juumre e tappirde %1$s:%2$s ɗemngal %3$s language.</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Nana ƴeewtoo saggitorde yahdiinde ngam aawtaade</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download">Waawaa seŋaade e sarworde Keyman ngam ƴeewtaade so saggitorde aawtoonde ena woodi</string>

View file

@ -80,8 +80,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file"> Librairie FileProvider nécessaire pour afficher le fichier d\'aide : %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> Erreur fatale de clavier avec %1$s:%2$s pour %3$s langue. Chargement du clavier par défaut.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> Erreur dans le clavier %1$s:%2$s pour la langue %3$s.</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Vérification du dictionnaire associé à télécharger</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> Impossible de se connecter au serveur Keyman pour chercher un dictionnaire à télécharger</string>

View file

@ -80,8 +80,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file">Ana buƙatar wajen karatu na FileProvider domin duba fayil ɗin taimako: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard">Matsalar madanni da %1$s:%2$s domin %3$s yaruka. Saukar da madanni mai kyau.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language).">Kuskure a yaren %1$s:%2$s for %3$s madanni.</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Duba ƙamus mai alaƙa domin a sauke</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> Cannot connect to Keyman server to check for associated dictionary to download</string>

View file

@ -82,8 +82,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file"> FileProvider library needed to view help file: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> Fatal keyboard nda kuskure %1$s:%2$s anga %3$s gwaɗaha luwa. Kalt girgira keyboard.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> Kuskure an keyboard %1$s:%2$s for %3$s gwaɗa luwa.</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Naghata anga gwaft dictionary da sangata</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> Cannot connect to Keyman server to check for associated dictionary to download</string>

View file

@ -78,8 +78,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file">Butuh FileProvider library untuk menampilkan berkas bantuan: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard">Terjadi masalah dengan papan tombol %1$s:%2$s untuk bahasa %3$s. Papan tombol diubah ke setelah baku.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> Error in keyboard %1$s:%2$s for %3$s language.</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Mengecek kamus yang terkait untuk diunduh</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> Cannot connect to Keyman server to check for associated dictionary to download</string>

View file

@ -80,8 +80,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file"> Libreria FileProvider necessaria per visualizzare il file d\'aiuto: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> Errore fatale della tastiera con %1$s:%2$s per %3$s lingua. Caricamento tastiera predefinita.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> Errore nella tastiera %1$s:%2$s per %3$s lingua.</string>
<!-- Context: Alert Dialog body: System WebView not installed -->
<string name="body_install_webview" comment="Notification when WebView not installed"> Keyman richiede che WebView sia installato.\"</string>
<!-- Context: Alert Dialog body: System WebView installed but not enabled -->

View file

@ -78,8 +78,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file"> ត្រូវការបណ្ណាល័យ FileProvider ដើម្បី​មើលឯកសារជំនួយ៖ %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> មាន​បញ្ហាធ្ងន់ធ្ងរចំពោះក្ដារចុច %1$s:%2$s សម្រាប់​ភាសា %3$s។ កំពុង​ផ្ទុក​ក្ដារចុច​លំនាំដើម។</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> បញ្ហាក្នុងក្ដារចុច %1$s:%2$s សម្រាប់ភាសា %3$s។</string>
<!-- Context: Alert Dialog body: System WebView not installed -->
<string name="body_install_webview" comment="Notification when WebView not installed">Keyman តម្រូវឱ្យដំឡើង WebView ។\"</string>
<!-- Context: Alert Dialog body: System WebView installed but not enabled -->

View file

@ -80,8 +80,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file">ಸಹಾಯ ಫೈಲ್ ವೀಕ್ಷಿಸಲು FileProvider ಲೈಬ್ರರಿ ಅಗತ್ಯವಿದೆ: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard">%1$s:%2$s ಕೀಲಿಮಣೆಯಲ್ಲಿ %3$s ಭಾಷೆಯೊಂದಿಗೆ ದೋಷವಿದೆ. ಅದರ ಬದಲಿಗೆ ಪೂರ್ವನಿರ್ಧರಿತ ಕೀಲಿಮಣೆ ಲೋಡ್ ಆಗುತ್ತಿದೆ.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language).">%1$s:%2$s ಕೀಲಿಮಣೆಯಲ್ಲಿ %3$s ಭಾಷೆಯೊಂದಿಗೆ ದೋಷವಿದೆ.</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">ಸಂಬಂಧಿಸಿದ ಶಬ್ದಕೋಶವನ್ನು ಡೌನ್‌ಲೋಡ್ ಮಾಡಲು ಪರಿಶೀಲಿಸಲಾಗುತ್ತಿದೆ</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download">ಸಂಬಂಧಿಸಿದ ಶಬ್ದಕೋಶವನ್ನು ಡೌನ್‌ಲೋಡ್ ಮಾಡಲು ಕೀಮ್ಯಾನ್ ಸರ್ವರ್ ಅನ್ನು ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲಾ</string>

View file

@ -80,8 +80,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file"> FileProvider library needed to view help file: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> Fatal keyboard error with %1$s:%2$s for %3$s language. Loading default keyboard.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language).">Taltǝ keyboard dǝn mbeji %1$s:%2$s for %3$s tǝlama.</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Dictionary shiro kǝllata mane download nzǝ diye</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> Cannot connect to Keyman server to check for associated dictionary to download</string>

View file

@ -80,8 +80,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file"> FileProvider library a kátá zhara file mla: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> Kuskure Fatal keyboard antara%1$s:%2$s gé %3$s nare. femhe nde default keyboard.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> Kuskure am keyboard %1$s:%2$s for %3$s nare.</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Zharanahe dictionary harzhe gé tsukwanahe</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> Cannot connect to Keyman server to check for associated dictionary to download</string>

View file

@ -78,8 +78,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file"> တၚ်နၚ်လိက် FileProvider မိက်ဂွံဗဵု ဝှါၚ်အရီုဗၚ်: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> ကဳဗုဒ်ယောၚ်ယာဇၞော်ဇၞော်နကဵု %1$s:%2$s သွက် ဘာသာ %3$s ။ ဂြဲပ္တိုန်မံၚ် ကဳဗုဒ်ပကတိ။</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> တၚ်ယောၚ်ယာ ပ္ဍဲကဳဗုဒ် %1$s:%2$s သွက် ဘာသာ %3$s</string>
<!-- Context: Alert Dialog body: System WebView not installed -->
<string name="body_install_webview" comment="Notification when WebView not installed">သ္ဂောံဂြဲဖျေံ Keyman ဂှ် ဒးသုၚ်စောဲ WebView ရောၚ်။</string>
<!-- Context: Alert Dialog body: System WebView installed but not enabled -->

View file

@ -80,8 +80,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file">FileProvider library needed to view help file: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> Fatal keyboard error with %1$s:%2$s for %3$s language. Loading default keyboard.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language).">Ŋya nur keyboard %1$s:%2$s anu %3$s kra kalkal.</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Luba dictionary kuku nagu acittu downloadi</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> Cannot connect to Keyman server to check for associated dictionary to download</string>

View file

@ -80,8 +80,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file"> FileProvider bibliotheek nodig om het hulpbestand te bekijken: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> Fatale toetsenbord fout met %1$s:%2$s voor %3$s taal. Standaard toetsenbord wordt geladen.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> Fout in toetsenbord %1$s:%2$s voor %3$s taal.</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Downloaden van bijbehorende woordenboek</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> Cannot connect to Keyman server to check for associated dictionary to download</string>

View file

@ -84,8 +84,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file"> Biblioteka FileProvider potrzebna, aby wyświetlić plik pomocy: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> Błąd krytycznej klawiatury z %1$s:%2$s dla %3$s języka. Ładowanie domyślnej klawiatury.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> Błąd w klawiaturze %1$s:%2$s dla %3$s języka.</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Sprawdzanie dla skojarzonego słownika do pobrania</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> Cannot connect to Keyman server to check for associated dictionary to download</string>

View file

@ -80,8 +80,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file"> Biblioteca de FileProvider necessária para ver o arquivo de ajuda: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> Erro fatal de teclado com %1$s:%2$s para %3$s idioma. Carregando o teclado padrão.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> Erro no teclado %1$s:%2$s para %3$s idioma.</string>
<!-- Context: Alert Dialog body: System WebView not installed -->
<string name="body_install_webview" comment="Notification when WebView not installed"> Keyman requer que o WebView seja instalado.\"</string>
<!-- Context: Alert Dialog body: System WebView installed but not enabled -->

View file

@ -84,8 +84,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file"> Библиотека файлового провайдера необходима для просмотра файла справки: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> Неустранимая ошибка клавиатуры %1$s:%2$s для %3$s языка. Загрузка по умолчанию клавиатуры.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> Ошибка в клавиатуре %1$s:%2$s для языка %3$s.</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Проверка соответствующего словаря для загрузки</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> Cannot connect to Keyman server to check for associated dictionary to download</string>

View file

@ -80,8 +80,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file"> Filleverantörsbibliotek behövs för att visa hjälpfilen: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> Kritiskt tangentbordsfel med %1$s:%2$s för %3$s språk. Laddar standardtangentbordet.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> Fel i tangentbordet %1$s:%2$s för %3$s språk.</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Söker efter tillhörande ordbok att ladda ner</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> Cannot connect to Keyman server to check for associated dictionary to download</string>

View file

@ -84,8 +84,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file"> Бібліотека FileProvider необхідна для перегляду файлу довідки: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> Критична помилка в клавіатурі з %1$s:%2$s для мови %3$s . Завантаження клавіатури за замовчуванням.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> Помилка в клавіатурі %1$s:%2$s для мови %3$s.</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Перевірка пов\'язаного словника для завантаження</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> Cannot connect to Keyman server to check for associated dictionary to download</string>

View file

@ -78,8 +78,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file">Cần có Thư viện FileProvider để xem phần trợ giúp file: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard">Lỗi bàn phím nghiêm trọng với %1$s: %2$s cho ngôn ngữ %3$s. Đang tải bàn phím mặc định.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language).">Lỗi trong bàn phím %1$s:%2$s cho ngôn ngữ %3$s.</string>
<!-- Context: Alert Dialog body: System WebView not installed -->
<string name="body_install_webview" comment="Notification when WebView not installed"> Keyman yêu cầu phải cài đặt WebView\".</string>
<!-- Context: Alert Dialog body: System WebView installed but not enabled -->

View file

@ -78,8 +78,6 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file"> 需要文件提供者文库来查看帮助文件: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard"> %1$s键盘错误:%2$s为%3$s 语言。正在加载默认键盘。</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> 对于 %3$s 语言,键盘出现错误 %1$s:%2$s 。</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">正在检查相关字典以下载</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> 无法连接到 Keyman 服务器以检查要下载的相关字典</string>

View file

@ -123,13 +123,9 @@
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file">
FileProvider library needed to view help file: %1$s</string>
<!-- Context: Fatal keyboard error. Will load default keyboard -->
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard">
Fatal keyboard error with %1$s:%2$s for %3$s language. Loading default keyboard.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language).">
Error in keyboard %1$s:%2$s for %3$s language.</string>
<string name="fatal_keyboard_error" comment="Displays as a toast. Generally caused by a script error in keyboard view context, may be caused by keyboard or KeymanWeb.">
Keyman encountered an error and will restart</string>
<!-- Context: Alert Dialog body: System WebView not installed -->
<string name="body_install_webview" comment="Notification when WebView not installed">

View file

@ -23,7 +23,7 @@ public class FileUtilsTest {
List<ShadowLog.LogItem> logs = ShadowLog.getLogs();
// The logs contain type 4, but we only care about type 6 for connection messages
Assert.assertEquals(2, logs.size());
Assert.assertEquals("Size did not match 2: " + logs.toString(), 2, logs.size());
Assert.assertEquals("Connection", logs.get(0).tag);
Assert.assertEquals("Initialization failed:\njava.net.MalformedURLException: no protocol: invalidURL", logs.get(0).msg);

View file

@ -1,4 +1,6 @@
#!/usr/bin/env bash
# Keyman is copyright (C) SIL Global. MIT License.
#
# Build Keyman Engine for Android using Keyman Web artifacts
#
## START STANDARD BUILD SCRIPT INCLUDE
@ -7,20 +9,9 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
. "$KEYMAN_ROOT/resources/build/utils.inc.sh"
# ################################ Main script ################################
# Definition of global compile constants
KEYMAN_ANDROID_ROOT="$KEYMAN_ROOT/android"
KEYMAN_WEB_ROOT="$KEYMAN_ROOT/web"
ENGINE_ASSETS="$KEYMAN_ANDROID_ROOT/KMEA/app/src/main/assets"
CONFIG="release"
BUILD_FLAGS="aR -x lint -x test" # Gradle build w/o test
TEST_FLAGS="-x aR lintRelease testRelease" # Gradle test w/o build
JUNIT_RESULTS="##teamcity[importData type='junit' path='keyman\android\KMEA\app\build\test-results\testReleaseUnitTest\']"
builder_describe "Builds Keyman Engine for Android." \
"@/web/src/app/webview" \
"@/common/web/sentry-manager" \
@ -30,78 +21,64 @@ builder_describe "Builds Keyman Engine for Android." \
"test Runs lint and unit tests." \
":engine Builds Engine"
# parse before describe_outputs to check debug flags
builder_parse "$@"
# Definition of global compile constants
if builder_is_debug_build; then
builder_heading "### Debug config ####"
CONFIG="debug"
BUILD_FLAGS="assembleDebug -x lintDebug -x testDebug"
TEST_FLAGS="-x assembleDebug lintDebug testDebug"
JUNIT_RESULTS="##teamcity[importData type='junit' path='keyman\android\KMEA\app\build\test-results\testDebugUnitTest\']"
else
BUILD_FLAGS="assembleRelease -x lint -x test" # Gradle build w/o test
TEST_FLAGS="-x assembleRelease lintRelease testRelease" # Gradle test w/o build
JUNIT_RESULTS="##teamcity[importData type='junit' path='keyman\android\KMEA\app\build\test-results\testReleaseUnitTest\']"
fi
if builder_is_ci_build; then
GRADLE_DAEMON=--no-daemon
else
GRADLE_DAEMON=
fi
builder_describe_outputs \
build:engine /android/KMEA/app/build/outputs/aar/keyman-engine-${CONFIG}.aar
#### Build
# Parse args
DAEMON_FLAG=
if builder_is_ci_build; then
DAEMON_FLAG=--no-daemon
fi
build:engine /android/KMEA/app/build/outputs/aar/keyman-engine-${BUILDER_CONFIGURATION}.aar
#### Build action definitions ####
# Check about cleaning artifact paths
if builder_start_action clean:engine; then
rm -rf "$KEYMAN_ROOT/android/KMEA/app/build/outputs"
builder_finish_action success clean:engine
fi
if builder_start_action configure:engine; then
builder_finish_action success configure:engine
fi
# Destinations that will need the keymanweb artifacts
if builder_start_action build:engine; then
do_build() {
local ENGINE_ASSETS="$KEYMAN_ROOT/android/KMEA/app/src/main/assets"
# Copy KeymanWeb artifacts
echo "Copying Keyman Web artifacts"
cp "$KEYMAN_WEB_ROOT/build/app/webview/$CONFIG/keymanweb-webview.js" "$ENGINE_ASSETS/keymanweb-webview.js"
cp "$KEYMAN_WEB_ROOT/build/app/webview/$CONFIG/keymanweb-webview.js.map" "$ENGINE_ASSETS/keymanweb-webview.js.map"
cp "$KEYMAN_WEB_ROOT/build/app/webview/$CONFIG/map-polyfill.js" "$ENGINE_ASSETS/map-polyfill.js"
cp "$KEYMAN_WEB_ROOT/build/app/resources/osk/ajax-loader.gif" "$ENGINE_ASSETS/ajax-loader.gif"
cp "$KEYMAN_WEB_ROOT/build/app/resources/osk/kmwosk.css" "$ENGINE_ASSETS/kmwosk.css"
cp "$KEYMAN_WEB_ROOT/build/app/resources/osk/globe-hint.css" "$ENGINE_ASSETS/globe-hint.css"
cp "$KEYMAN_WEB_ROOT/build/app/resources/osk/keymanweb-osk.ttf" "$ENGINE_ASSETS/keymanweb-osk.ttf"
builder_echo "Copying Keyman Web artifacts"
cp "$KEYMAN_ROOT/web/build/app/webview/$BUILDER_CONFIGURATION/keymanweb-webview.js" "$ENGINE_ASSETS/"
cp "$KEYMAN_ROOT/web/build/app/webview/$BUILDER_CONFIGURATION/keymanweb-webview.js.map" "$ENGINE_ASSETS/"
cp "$KEYMAN_ROOT/web/build/app/webview/$BUILDER_CONFIGURATION/map-polyfill.js" "$ENGINE_ASSETS/"
cp "$KEYMAN_ROOT/web/build/app/resources/osk/ajax-loader.gif" "$ENGINE_ASSETS/"
cp "$KEYMAN_ROOT/web/build/app/resources/osk/kmwosk.css" "$ENGINE_ASSETS/"
cp "$KEYMAN_ROOT/web/build/app/resources/osk/globe-hint.css" "$ENGINE_ASSETS/"
cp "$KEYMAN_ROOT/web/build/app/resources/osk/keymanweb-osk.ttf" "$ENGINE_ASSETS/"
cp "$KEYMAN_ROOT/common/web/sentry-manager/build/lib/index.js" "$ENGINE_ASSETS/keyman-sentry.js"
echo "Copying es6-shim polyfill"
cp "$KEYMAN_ROOT/node_modules/es6-shim/es6-shim.min.js" "$ENGINE_ASSETS/es6-shim.min.js"
builder_echo "Copying es6-shim polyfill"
cp "$KEYMAN_ROOT/node_modules/es6-shim/es6-shim.min.js" "$ENGINE_ASSETS/"
builder_echo "BUILD_FLAGS $BUILD_FLAGS"
echo "BUILD_FLAGS $BUILD_FLAGS"
# Build without test
./gradlew $DAEMON_FLAG clean $BUILD_FLAGS
builder_finish_action success build:engine
fi
if builder_start_action test:engine; then
./gradlew $GRADLE_DAEMON $BUILD_FLAGS
}
do_test() {
if builder_is_ci_build; then
# Report JUnit test results to CI
echo "$JUNIT_RESULTS"
fi
echo "TEST_FLAGS: $TEST_FLAGS"
./gradlew $DAEMON_FLAG $TEST_FLAGS
builder_echo "TEST_FLAGS: $TEST_FLAGS"
./gradlew $GRADLE_DAEMON $TEST_FLAGS
}
builder_finish_action success test:engine
fi
builder_run_action clean:engine rm -rf build app/build
builder_run_action build:engine do_build
builder_run_action test:engine do_test

View file

@ -2,6 +2,9 @@ plugins {
id 'com.android.application'
}
ext.rootPath = '../../../'
apply from: "$rootPath/version.gradle"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
@ -17,13 +20,22 @@ android {
noCompress "kmp"
}
buildFeatures {
buildConfig = true
}
defaultConfig {
applicationId "com.keyman.kmsample1"
minSdkVersion 21
targetSdkVersion 35
versionCode 1
versionName "1.0"
// KEYMAN_VERSION_CODE, KEYMAN_VERSION_NAME, KEYMAN_VERSION_FOR_FILENAME from version.gradle
versionCode KEYMAN_VERSION_CODE as Integer
versionName KEYMAN_VERSION_NAME
buildConfigField "String", "KEYMAN_VERSION_ENVIRONMENT", "\""+KEYMAN_VERSION_ENVIRONMENT+"\""
archivesBaseName = "KMSample1-$KEYMAN_VERSION_FOR_FILENAME"
}
buildTypes {
release {
minifyEnabled false
@ -40,7 +52,6 @@ android {
repositories {
google()
}
dependencies {

View file

@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- allow Sentry reports -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application
android:allowBackup="true"

View file

@ -64,6 +64,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
KMManager.KMDefault_KeyboardFont, // Font for KMSample1 text field
KMManager.KMDefault_KeyboardFont); // Font for OSK
KMManager.addKeyboard(this, kbInfo);
KMManager.setDefaultKeyboard(kbInfo); // Workaround for #16215
// Add a dictionary
HashMap<String, String>lexicalModelInfo = new HashMap<String, String>();
@ -132,7 +133,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
KMManager.onConfigurationChanged(newConfig);
resizeTextView(textView.isKeyboardVisible());
lastOrientation = newConfig.orientation;
}

View file

@ -3,7 +3,6 @@
buildscript {
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
@ -17,7 +16,6 @@ buildscript {
allprojects {
repositories {
google()
jcenter()
mavenCentral()
}
}

View file

@ -1,21 +1,16 @@
#!/usr/bin/env bash
# Keyman is copyright (C) SIL Global. MIT License.
#
# Samples: KMsample1
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
. "$KEYMAN_ROOT/resources/build/utils.inc.sh"
. "${KEYMAN_ROOT}/android/android-utils.inc.sh"
################################ Main script ################################
# Definition of global compile constants
CONFIG="release"
SAMPLE_FLAGS="build"
builder_describe "Build KMSample1 app for Android." \
"@/android/KMEA" \
"clean" \
@ -24,55 +19,19 @@ builder_describe "Build KMSample1 app for Android." \
"test" \
":app KMSample1"
# parse before describe_outputs to check debug flags
builder_parse "$@"
ARTIFACT="app-release-unsigned.apk"
if builder_is_debug_build; then
builder_heading "### Debug config ####"
CONFIG="debug"
SAMPLE_FLAGS="assembleDebug"
ARTIFACT="app-$CONFIG.apk"
fi
android_set_gradle_environment
builder_describe_outputs \
build:app /android/Samples/KMSample1/app/build/outputs/apk/$CONFIG/$ARTIFACT
# Parse args
if builder_is_ci_build; then
SAMPLE_FLAGS="$SAMPLE_FLAGS -no-daemon"
fi
build:app "/android/Samples/KMSample1/app/build/outputs/apk/${BUILDER_CONFIGURATION}/KMSample1-${KEYMAN_VERSION_FOR_FILENAME}-${ARCHIVE_TARGET}.apk"
#### Build action definitions ####
# Check about cleaning artifact paths
if builder_start_action clean:app; then
rm -rf "$KEYMAN_ROOT/android/Samples/KMSample1/app/build/outputs"
builder_finish_action success clean:app
fi
do_build() {
cp "$KEYMAN_ROOT/android/KMEA/app/build/outputs/aar/keyman-engine-${BUILDER_CONFIGURATION}.aar" "$KEYMAN_ROOT/android/Samples/KMSample1/app/libs/keyman-engine.aar"
./gradlew $GRADLE_DAEMON $GRADLE_TARGET
}
if builder_start_action configure:app; then
builder_finish_action success configure:app
fi
# Building KMSample1
if builder_start_action build:app; then
# Copy Keyman Engine for Android
cp "$KEYMAN_ROOT/android/KMEA/app/build/outputs/aar/keyman-engine-${CONFIG}.aar" "$KEYMAN_ROOT/android/Samples/KMSample1/app/libs/keyman-engine.aar"
./gradlew clean $SAMPLE_FLAGS
builder_finish_action success build:app
fi
if builder_start_action test:app; then
# TODO: define tests
builder_finish_action success test:app
fi
builder_run_action clean:app rm -rf build app/build
builder_run_action build:app do_build

View file

@ -2,6 +2,9 @@ plugins {
id 'com.android.application'
}
ext.rootPath = '../../../'
apply from: "$rootPath/version.gradle"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
@ -17,13 +20,22 @@ android {
noCompress "kmp"
}
buildFeatures {
buildConfig = true
}
defaultConfig {
applicationId "com.keyman.kmsample2"
minSdkVersion 21
targetSdkVersion 35
versionCode 1
versionName "1.0"
// KEYMAN_VERSION_CODE, KEYMAN_VERSION_NAME, KEYMAN_VERSION_FOR_FILENAME from version.gradle
versionCode KEYMAN_VERSION_CODE as Integer
versionName KEYMAN_VERSION_NAME
buildConfigField "String", "KEYMAN_VERSION_ENVIRONMENT", "\""+KEYMAN_VERSION_ENVIRONMENT+"\""
archivesBaseName = "KMSample2-$KEYMAN_VERSION_FOR_FILENAME"
}
buildTypes {
release {
minifyEnabled false

View file

@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- allow Sentry reports -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application
android:allowBackup="true"

View file

@ -57,6 +57,7 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
KMManager.KMDefault_KeyboardFont, // Font for KMSample2
KMManager.KMDefault_KeyboardFont); // Font for OSK
KMManager.addKeyboard(this, kbInfo);
KMManager.setDefaultKeyboard(kbInfo); // Workaround for #16215
// Add a dictionary
HashMap<String, String>lexicalModelInfo = new HashMap<String, String>();
@ -262,4 +263,4 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
return interpreter.onKeyLongPress(keyCode, event);
}
}
}

View file

@ -3,7 +3,6 @@
buildscript {
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
@ -17,7 +16,6 @@ buildscript {
allprojects {
repositories {
google()
jcenter()
mavenCentral()
}
}

View file

@ -1,21 +1,16 @@
#!/usr/bin/env bash
# Keyman is copyright (C) SIL Global. MIT License.
#
# Samples: KMSample2
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
. "$KEYMAN_ROOT/resources/build/utils.inc.sh"
. "${KEYMAN_ROOT}/android/android-utils.inc.sh"
################################ Main script ################################
# Definition of global compile constants
CONFIG="release"
SAMPLE_FLAGS="build"
builder_describe "Build KMSample2 app for Android." \
"@/android/KMEA" \
"clean" \
@ -24,55 +19,19 @@ builder_describe "Build KMSample2 app for Android." \
"test" \
":app KMSample2"
# parse before describe_outputs to check debug flags
builder_parse "$@"
ARTIFACT="app-release-unsigned.apk"
if builder_is_debug_build; then
builder_heading "### Debug config ####"
CONFIG="debug"
SAMPLE_FLAGS="assembleDebug"
ARTIFACT="app-$CONFIG.apk"
fi
android_set_gradle_environment
builder_describe_outputs \
build:app /android/Samples/KMSample2/app/build/outputs/apk/$CONFIG/$ARTIFACT
# Parse args
if builder_is_ci_build; then
SAMPLE_FLAGS="$SAMPLE_FLAGS -no-daemon"
fi
build:app "/android/Samples/KMSample2/app/build/outputs/apk/${BUILDER_CONFIGURATION}/KMSample2-${KEYMAN_VERSION_FOR_FILENAME}-${ARCHIVE_TARGET}.apk"
#### Build action definitions ####
# Check about cleaning artifact paths
if builder_start_action clean:app; then
rm -rf "$KEYMAN_ROOT/android/Samples/KMSample2/app/build/outputs"
builder_finish_action success clean:app
fi
do_build() {
cp "$KEYMAN_ROOT/android/KMEA/app/build/outputs/aar/keyman-engine-${BUILDER_CONFIGURATION}.aar" "$KEYMAN_ROOT/android/Samples/KMSample2/app/libs/keyman-engine.aar"
./gradlew $GRADLE_DAEMON $GRADLE_TARGET
}
if builder_start_action configure:app; then
builder_finish_action success configure:app
fi
# Building KMSample2
if builder_start_action build:app; then
# Copy Keyman Engine for Android
cp "$KEYMAN_ROOT/android/KMEA/app/build/outputs/aar/keyman-engine-${CONFIG}.aar" "$KEYMAN_ROOT/android/Samples/KMSample2/app/libs/keyman-engine.aar"
./gradlew clean $SAMPLE_FLAGS
builder_finish_action success build:app
fi
if builder_start_action test:app; then
# TODO: define tests
builder_finish_action success test:app
fi
builder_run_action clean:app rm -rf build app/build
builder_run_action build:app do_build

View file

@ -29,10 +29,11 @@ android {
minSdkVersion 21
targetSdkVersion 35
// KEYMAN_VERSION_CODE and KEYMAN_VERSION_NAME from version.gradle
// KEYMAN_VERSION_CODE, KEYMAN_VERSION_NAME, KEYMAN_VERSION_FOR_FILENAME from version.gradle
versionCode KEYMAN_VERSION_CODE as Integer
versionName KEYMAN_VERSION_NAME
buildConfigField "String", "KEYMAN_VERSION_ENVIRONMENT", "\""+KEYMAN_VERSION_ENVIRONMENT+"\""
archivesBaseName = "KeyboardHarness-$KEYMAN_VERSION_FOR_FILENAME"
}
buildTypes {
release {

View file

@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- allow Sentry reports -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application
android:allowBackup="true"

View file

@ -1,21 +1,16 @@
#!/usr/bin/env bash
# Keyman is copyright (C) SIL Global. MIT License.
#
# Build Test app: KeyboardHarness
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
. "$KEYMAN_ROOT/resources/build/utils.inc.sh"
. "${KEYMAN_ROOT}/android/android-utils.inc.sh"
################################ Main script ################################
# Definition of global compile constants
CONFIG="release"
BUILD_FLAGS="aR -x lint -x test" # Gradle build w/o test
TEST_FLAGS="-x aR lintRelease testRelease" # Gradle test w/o build
builder_describe "Build KeyboardHarness test app for Android." \
"@/android/KMEA" \
"clean" \
@ -24,66 +19,19 @@ builder_describe "Build KeyboardHarness test app for Android." \
"test" \
":app KeyboardHarness"
# parse before describe outputs to check debug flags
builder_parse "$@"
ARTIFACT="app-release-unsigned.apk"
if builder_is_debug_build; then
builder_heading "### Debug config ####"
CONFIG="debug"
BUILD_FLAGS="assembleDebug -x lintDebug -x testDebug"
TEST_FLAGS="-x assembleDebug lintDebug testDebug"
ARTIFACT="app-$CONFIG.apk"
fi
android_set_gradle_environment
builder_describe_outputs \
build:app /android/Tests/KeyboardHarness/app/build/outputs/apk/$CONFIG/${ARTIFACT}
#### Build
#
# Prevents 'clear' on exit of mingw64 bash shell
#
SHLVL=0
# Parse args
# Build flags that apply to all targets
if builder_is_ci_build; then
BUILD_FLAGS="$BUILD_FLAGS -no-daemon"
TEST_FLAGS="$TEST_FLAGS -no-daemon"
fi
build:app "/android/Tests/KeyboardHarness/app/build/outputs/apk/${BUILDER_CONFIGURATION}/KeyboardHarness-${KEYMAN_VERSION_FOR_FILENAME}-${ARCHIVE_TARGET}.apk"
#### Build action definitions ####
# Check about cleaning artifact paths
if builder_start_action clean:app; then
rm -rf "$KEYMAN_ROOT/android/Tests/KeyboardHarness/app/build/outputs"
builder_finish_action success clean:app
fi
do_build() {
cp "$KEYMAN_ROOT/android/KMEA/app/build/outputs/aar/keyman-engine-${BUILDER_CONFIGURATION}.aar" ./app/libs/keyman-engine.aar
./gradlew $GRADLE_DAEMON $GRADLE_TARGET
}
if builder_start_action configure:app; then
builder_finish_action success configure:app
fi
# Building KeyboardHarness
if builder_start_action build:app; then
# Copy Keyman Engine for Android
cp "$KEYMAN_ROOT/android/KMEA/app/build/outputs/aar/keyman-engine-${CONFIG}.aar" "$KEYMAN_ROOT/android/Tests/KeyboardHarness/app/libs/keyman-engine.aar"
echo "BUILD_FLAGS: $BUILD_FLAGS"
./gradlew clean $BUILD_FLAGS
builder_finish_action success build:app
fi
if builder_start_action test:app; then
echo "TEST_FLAGS $TEST_FLAGS"
# TODO: define tests
builder_finish_action success test:app
fi
builder_run_action clean:app rm -rf build app/build
builder_run_action build:app do_build

View file

@ -0,0 +1,20 @@
# shellcheck shell=bash
# Keyman is copyright (C) SIL Global. MIT License.
function android_set_gradle_environment() {
if builder_is_debug_build; then
GRADLE_TARGET=assembleDebug
ARCHIVE_TARGET=debug
else
GRADLE_TARGET=build
ARCHIVE_TARGET=release-unsigned
fi
if builder_is_ci_build; then
GRADLE_DAEMON=-no-daemon
else
GRADLE_DAEMON=
fi
export GRADLE_TARGET GRADLE_DAEMON ARCHIVE_TARGET
}

View file

@ -1,4 +1,5 @@
#!/usr/bin/env bash
# Keyman is copyright (C) SIL Global. MIT License.
#
# Build Keyman Engine for Android, Keyman for Android, OEM FirstVoices Android app,
# Samples: KMsample1 and KMSample2, Test - KeyboardHarness
@ -50,11 +51,6 @@ builder_describe \
builder_parse "$@"
function do_clean() {
builder_heading "Cleanup /android/upload"
rm -rf "${KEYMAN_ROOT}/android/upload"
}
function do_test_help() {
check-markdown "${KEYMAN_ROOT}/android/docs/help"
check-markdown "${KEYMAN_ROOT}/android/docs/engine"
@ -62,7 +58,6 @@ function do_test_help() {
function archive_artifacts() {
local UPLOAD_PATH KEYMAN_ENGINE_ANDROID_ZIP KEYMAN_APK FIRSTVOICES_APK
local KEYMAN_FULLY_VERSIONED_APK FIRSTVOICES_FULLY_VERSIONED_APK
UPLOAD_PATH="${KEYMAN_ROOT}/android/upload/${KEYMAN_VERSION}"
KEYMAN_ENGINE_ANDROID_ZIP="keyman-engine-android-${KEYMAN_VERSION}.zip"
@ -124,12 +119,13 @@ android_set_java_home
# This script also responsible for cleaning up /android/upload
builder_run_child_actions clean
builder_run_action clean do_clean
builder_run_action clean rm -rf "${KEYMAN_ROOT}/android/upload"
builder_run_child_actions configure build test
builder_run_action test:help do_test_help
builder_run_action test:help do_test_help
# TODO: merge publish-* and archive to publish action?
builder_run_child_actions publish-symbols publish-play-store
builder_run_action archive archive_artifacts
builder_run_action archive archive_artifacts

View file

@ -32,39 +32,6 @@ can be selected from the keyboards menu. If the keyboard with same
keyboard ID and language ID exists, it updates the existing keyboard
info.
------------------------------------------------------------------------
## Syntax (Deprecated)
``` javascript
KMManager.addKeyboard(Context context, HashMap<String, String> keyboardInfo)
```
### Parameters
`context`
: The context.
`keyboardInfo`
: A dictionary of keyboard information with keys and values defined as
`HashMap<String key, String value>`.
### Returns
Returns `true` if the keyboard was added successfully, `false`
otherwise.
## Description
Use this method to include a keyboard in the keyboards list so that it
can be selected from the keyboards menu. If the keyboard with same
keyboard ID and language ID exists, it updates the existing keyboard
info.
------------------------------------------------------------------------
## Examples

View file

@ -28,34 +28,6 @@ Use this method to get details of the currently selected keyboard.
Details include package ID, keyboard ID, language ID, keyboard name,
language name and fonts.
------------------------------------------------------------------------
## Syntax (Deprecated)
``` javascript
KMManager.getCurrentKeyboardInfo(Context context)
```
### Parameters
`context`
: The context.
### Returns
(Deprecated) Returns an information dictionary of the current keyboard
with keys and values defined as `HashMap<String key, String value>`.
## Description
Use this method to get details of the currently selected keyboard.
Details include keyboard ID, language ID, keyboard name, language name
and fonts.
------------------------------------------------------------------------
## Examples

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

@ -22,6 +22,8 @@ empty string otherwise.
Use this method to get the font filename of the selected keyboard.
Deprecated. Use `getKeyboardTextFontFilename()` instead.
## Examples
### Example: Using `getKeyboardFontFilename()`
@ -34,5 +36,6 @@ The following script illustrate the use of `getKeyboardFontFilename()`:
## See also
- [`getKeyboardTextFontFilename()`](getKeyboardTextFontFilename)
- [`getKeyboardFontTypeface()` (Deprecated)](getKeyboardFontTypeface)
- [`getFontTypeface()`](getFontTypeface)

View file

@ -28,6 +28,8 @@ exists, `null` otherwise.
Use this method to create a new typeface from the selected keyboard's
font if it has any.
Deprecated. Use `getKeyboardTextFontFilename()` instead.
## Examples
### Example: Using `getKeyboardFontTypeface()`
@ -44,3 +46,4 @@ The following script illustrate the use of `getKeyboardFontTypeface()`:
- [`getFontTypeface()`](getFontTypeface)
- [`getKeyboardFontFilename()` (Deprecated)](getKeyboardFontFilename)
- [`getKeyboardTextFontFilename()`](getKeyboardTextFontFilename)

View file

@ -31,35 +31,6 @@ Use this method to get details of the keyboard at given position in
keyboards list. Details include keyboard ID, language ID, keyboard name,
language name and fonts.
------------------------------------------------------------------------
## Syntax (Deprecated)
``` javascript
KMManager.getKeyboardInfo(Context context, int index)
```
### Parameters
`context`
: The context.
`index`
: 0-based position of the keyboard in keyboards list.
### Returns
(Deprecated) Returns an information dictionary of the specified keyboard
with keys and values defined as `HashMap<String key, String value>`.
## Description
Use this method to get details of the keyboard at given position in
keyboards list. Details include keyboard ID, language ID, keyboard name,
language name and fonts.
## Examples
### Example: Using `getKeyboardInfo()`

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

View file

@ -26,33 +26,6 @@ otherwise.
Use this method to get details of all keyboard's in keyboards menu.
------------------------------------------------------------------------
## Syntax (Deprecated)
``` javascript
KMManager.getKeyboardsList(Context context)
```
### Parameters
`context`
: The context.
### Returns
(Deprecated) Returns keyboards list as
`ArrayList<HashMap<String key, String value>>` if it exists, `null`
otherwise.
## Description
Use this method to get details of all keyboard's in keyboards menu.
------------------------------------------------------------------------
## Examples

View file

@ -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 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.
`kFont`
: Filename or description of the font to type with the keyboard. Can be `null`
`textFontFilename`
: 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`
or empty string.
`oskFontFilename`
: Filename of the font displayed on the keyboard. Can be `null`
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`,

View file

@ -88,6 +88,16 @@ def getVersionGitTag = { ->
return "release@$KEYMAN_VERSION_NAME"
}
}
def getVersionForFilename = { ->
String env_version_for_filename = System.getenv("KEYMAN_VERSION_FOR_FILENAME")
if(env_version_for_filename != null) {
return env_version_for_filename
}
// Building probably from IDE
return "$KEYMAN_VERSION_NAME"
}
ext {
KEYMAN_VERSION_CODE=getVersionCode()
KEYMAN_VERSION_MD=getVersionMD()
@ -95,6 +105,7 @@ ext {
KEYMAN_VERSION_ENVIRONMENT=getVersionEnvironment()
KEYMAN_VERSION_TAG=System.getenv("KEYMAN_VERSION_TAG")
KEYMAN_VERSION_GIT_TAG=getVersionGitTag()
KEYMAN_VERSION_FOR_FILENAME=getVersionForFilename()
}
//println "version.gradle: KEYMAN_VERSION_TAG: " + KEYMAN_VERSION_TAG

Some files were not shown because too many files have changed in this diff Show more