mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-05 00:15:32 +00:00
Merge branch 'change/web/multi-token-correction-search' into change/web/start-multi-tokenization
Some checks failed
Keyman Build Summary / Summarize build status checks (push) Has been cancelled
Some checks failed
Keyman Build Summary / Summarize build status checks (push) Has been cancelled
This commit is contained in:
commit
0106fbdaee
452 changed files with 7518 additions and 4006 deletions
4
.github/workflows/api-verification.yml
vendored
4
.github/workflows/api-verification.yml
vendored
|
|
@ -71,7 +71,7 @@ jobs:
|
|||
- name: "Verify API for libkeymancore*.so (${{ steps.environment_step.outputs.GIT_BRANCH }}, branch ${{ steps.environment_step.outputs.GIT_BASE_BRANCH }}, by ${{ steps.environment_step.outputs.GIT_USER }})"
|
||||
if: steps.environment_step.outputs.SKIP_API_CHECK != 'true'
|
||||
run: |
|
||||
echo "Verify API for libkeymancore*.so (${{ steps.environment_step.outputs.GIT_BRANCH }}, branch ${{ steps.environment_step.outputs.GIT_BASE_BRANCH }}, by ${{ steps.environment_step.outputs.GIT_USER }}):" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Verify API for libkeymancore*.so (${{ steps.environment_step.outputs.GIT_BRANCH }}, branch ${{ steps.environment_step.outputs.GIT_BASE_BRANCH }}, by ${{ steps.environment_step.outputs.GIT_USER }}):" | tee -a ${GITHUB_STEP_SUMMARY}
|
||||
|
||||
BIN_PACKAGE=$(ls "${GITHUB_WORKSPACE}/artifacts/" | grep "${PKG_NAME}[0-9]*_${{ steps.environment_step.outputs.KEYMAN_VERSION }}-1${{ steps.environment_step.outputs.PRERELEASE_TAG }}+$(lsb_release -c -s)1_amd64.deb")
|
||||
cd ${{ github.workspace }}/keyman/linux
|
||||
|
|
@ -80,7 +80,7 @@ jobs:
|
|||
--bin-pkg "${GITHUB_WORKSPACE}/artifacts/${BIN_PACKAGE}" \
|
||||
--git-sha "${{ steps.environment_step.outputs.GIT_SHA }}" \
|
||||
--git-base "${{ steps.environment_step.outputs.GIT_BASE }}" \
|
||||
verify 2>> $GITHUB_STEP_SUMMARY
|
||||
verify 2> >(tee -a ${GITHUB_STEP_SUMMARY} >&2)
|
||||
|
||||
- name: Archive .symbols file
|
||||
if: steps.environment_step.outputs.SKIP_API_CHECK != 'true' && always()
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ jobs:
|
|||
- name: Install meson
|
||||
shell: bash
|
||||
run: |
|
||||
pip3 install --user meson==1.10.1
|
||||
pip3 install --user meson==1.11.0
|
||||
|
||||
- name: Run test
|
||||
shell: bash
|
||||
|
|
|
|||
2
.github/workflows/deb-packaging.yml
vendored
2
.github/workflows/deb-packaging.yml
vendored
|
|
@ -137,7 +137,7 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
dist: [jammy, noble, questing]
|
||||
dist: [jammy, noble, resolute]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
|
|
|||
4
.github/workflows/labeler.yml
vendored
4
.github/workflows/labeler.yml
vendored
|
|
@ -8,12 +8,12 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Update labels based on changed files
|
||||
uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1
|
||||
uses: actions/labeler@f27b608878404679385c85cfa523b85ccb86e213 # v6.1.0
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
- name: Update labels based on PR title
|
||||
id: labeler
|
||||
uses: fuxingloh/multi-labeler@f5bd7323b53b0833c1e4ed8d7b797ae995ef75b4 # v2.0.1
|
||||
uses: fuxingloh/multi-labeler@bcd50af464202999e57f556b4aefcf05a34abf85 # v5.0.0
|
||||
with:
|
||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||
config-path: .github/multi-labeler.yml
|
||||
|
|
|
|||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -186,3 +186,7 @@ lcov.info
|
|||
|
||||
# flag file for build script
|
||||
.configured
|
||||
|
||||
# see common/tools/api-extractor/README.md
|
||||
tsdoc.json
|
||||
api-extractor.json
|
||||
|
|
|
|||
116
HISTORY.md
116
HISTORY.md
|
|
@ -1,5 +1,121 @@
|
|||
# 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)
|
||||
|
||||
## 19.0.250 alpha 2026-07-01
|
||||
|
||||
* fix(android): use `WebViewAssetLoader` for loading assets on Android (address regression in 19.0.242) (#16146)
|
||||
* fix(ios): bundle in KMW's globe-hint.css (#16142)
|
||||
* fix(ios): load host page, keyboards through a consistent WKURLSchemeHandler (#16136)
|
||||
|
||||
## 19.0.249 alpha 2026-06-30
|
||||
|
||||
* fix(windows): add update property to remote check (#16126)
|
||||
* chore(android): allow to build FV app in docker container (#16163)
|
||||
* fix(developer): warn only on race when destroying TAppSourceHttpResponder (#16140)
|
||||
* fix(developer): map shift key nextlayer property when importing OSK (#16110)
|
||||
|
||||
## 19.0.248 alpha 2026-06-29
|
||||
|
||||
* chore: add missing line to history (#16154)
|
||||
* maint(resources): DRY out `PRInformation` interface (#16158)
|
||||
* fix(developer): prevent clone of legacy keyboards with no source (#16111)
|
||||
* fix(developer): consolidate user options in TypeScript code (#16134)
|
||||
* fix(developer): fixup references to layer after deleting in Touch Layout Editor (#16129)
|
||||
* fix(web): revert regression in setting `activeKeyboard` in `set osk` (#16156)
|
||||
* test(web): add e2e tests for examples from guide (#16108)
|
||||
* maint(linux): show output of API check also in log file (#16153)
|
||||
|
||||
## 19.0.247 alpha 2026-06-16
|
||||
|
||||
* chore: update multi-labeler to 5.0.0 (#16100)
|
||||
|
||||
## 19.0.246 alpha 2026-06-15
|
||||
|
||||
* feat(developer): run project validation from IDE (#16076)
|
||||
* fix(core): use meson subsystem and needs_exe_wrapper for cross builds on Windows (#16088)
|
||||
* docs(windows): add context documentation for additional Advanced options (#16089)
|
||||
|
||||
## 19.0.245 alpha 2026-06-10
|
||||
|
||||
* chore(web): hide KMX keyboard test page (#16083)
|
||||
* docs(web): remove unnecessary mentioning of Keyman 17 from docs (#16085)
|
||||
* chore(web): remove outdated `*_load.js` files (#16086)
|
||||
* fix(web): fix `KeyboardStub.validateForCustomKeyboard` (#16067)
|
||||
|
||||
## 19.0.244 alpha 2026-06-09
|
||||
|
||||
* refactor(web): extract function, rename variables (#16063)
|
||||
* refactor(web): reformatting of files (#16066)
|
||||
* chore(web): improve the KeymanWeb integration documentation (#16064)
|
||||
* chore(web): more improvements to documentation (#16070)
|
||||
|
||||
## 19.0.243 alpha 2026-06-08
|
||||
|
||||
* fix(developer): reduce km_core_keyboard_attrs size to 12 (#16075)
|
||||
* chore(deps): bump minimatch in /core/tests/unit/wasm (#16045)
|
||||
* chore(deps): bump glob from 10.4.5 to 10.5.0 in /core/tests/unit/wasm (#16046)
|
||||
* chore(deps-dev): bump js-yaml from 4.1.0 to 4.2.0 in /core/tests/unit/wasm (#16047)
|
||||
* chore(deps-dev): bump picomatch from 2.3.1 to 2.3.2 in /core/tests/unit/wasm (#16048)
|
||||
* chore(deps): bump qs and express (#16009)
|
||||
|
||||
## 19.0.242 alpha 2026-06-03
|
||||
|
||||
* fix(web): fix displaying of keyboard menu (#16061)
|
||||
|
||||
## 19.0.241 alpha 2026-06-02
|
||||
|
||||
* chore: web-core preflight (#16015)
|
||||
* chore(web): web-core preflight - strip core references (#16040)
|
||||
* docs: add note on how to use composer on dockerized websites (#16029)
|
||||
* fix(web): fix race displaying active keyboard in menu (#16042)
|
||||
|
||||
## 19.0.240 alpha 2026-05-28
|
||||
|
||||
* chore(web): add filename to link text of manual web tests (#16019)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
19.0.241
|
||||
19.0.259
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
@ -173,10 +172,11 @@ dependencies {
|
|||
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
|
||||
implementation 'com.google.android.material:material:1.12.0'
|
||||
implementation 'com.stepstone.stepper:material-stepper:4.3.1'
|
||||
implementation 'androidx.webkit:webkit:1.14.0'
|
||||
implementation files('libs/keyman-engine.aar')
|
||||
implementation 'io.sentry:sentry-android:8.19.1'
|
||||
implementation 'androidx.preference:preference:1.2.1'
|
||||
implementation "com.android.installreferrer:installreferrer:2.2"
|
||||
implementation 'com.android.installreferrer:installreferrer:2.2'
|
||||
|
||||
// Add dependency for generating QR Codes
|
||||
// (Even though it's embedded in KMEA, because we're manually copying keyman-engine.aar,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
||||
|
|
@ -72,6 +72,7 @@ dependencies {
|
|||
implementation 'commons-io:commons-io:2.16.1'
|
||||
implementation 'io.sentry:sentry-android:8.19.1'
|
||||
implementation 'androidx.preference:preference:1.2.1'
|
||||
implementation 'androidx.webkit:webkit:1.14.0'
|
||||
|
||||
// Robolectric
|
||||
testImplementation 'androidx.test.ext:junit:1.2.1'
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Copyright (C) 2017-2018 SIL International. All rights reserved.
|
||||
/*
|
||||
* Keyman is copyright (C) SIL Global. MIT License.
|
||||
*/
|
||||
|
||||
package com.keyman.engine;
|
||||
|
|
@ -23,6 +23,7 @@ import com.keyman.engine.util.DependencyUtil.LibraryType;
|
|||
import com.keyman.engine.util.FileUtils;
|
||||
import com.keyman.engine.util.KMLog;
|
||||
import com.keyman.engine.util.KMString;
|
||||
import com.keyman.engine.util.WebViewUtils;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
|
|
@ -83,9 +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 keyboardRoot = "";
|
||||
private static String textFontPath = "";
|
||||
private static String oskFontPath = "";
|
||||
private final String fontUndefined = "undefined";
|
||||
private GestureDetector gestureDetector;
|
||||
private static ArrayList<OnKeyboardEventListener> kbEventListeners = null;
|
||||
|
|
@ -310,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;
|
||||
|
|
@ -366,8 +366,7 @@ final class KMKeyboard extends WebView {
|
|||
} else {
|
||||
KMManager.SystemKeyboardWebViewClient.setKeyboardLoaded(false);
|
||||
}
|
||||
|
||||
String htmlPath = "file://" + getContext().getDir("data", Context.MODE_PRIVATE) + "/" + KMManager.KMFilename_KeyboardHtml;
|
||||
String htmlPath = WebViewUtils.buildAssetUrl(KMManager.KMFilename_KeyboardHtml);
|
||||
loadUrl(htmlPath);
|
||||
setBackgroundColor(0);
|
||||
}
|
||||
|
|
@ -550,7 +549,7 @@ final class KMKeyboard extends WebView {
|
|||
* @return String
|
||||
*/
|
||||
public static String textFontFilename() {
|
||||
return txtFont;
|
||||
return textFontPath;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -558,9 +557,10 @@ final class KMKeyboard extends WebView {
|
|||
* @return String
|
||||
*/
|
||||
public static String oskFontFilename() {
|
||||
return oskFont;
|
||||
return oskFontPath;
|
||||
}
|
||||
|
||||
// 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
|
||||
|
|
@ -601,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 {
|
||||
|
|
@ -632,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();
|
||||
|
|
@ -646,8 +650,6 @@ final class KMKeyboard extends WebView {
|
|||
}
|
||||
String kbKey = KMString.format("%s_%s", languageID, keyboardID);
|
||||
|
||||
setKeyboardRoot(packageID);
|
||||
|
||||
// Escape single-quoted names for javascript call
|
||||
keyboardName = keyboardName.replaceAll("\'", "\\\\'"); // Double-escaped-backslash b/c regex.
|
||||
|
||||
|
|
@ -662,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;
|
||||
}
|
||||
|
|
@ -686,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
|
||||
|
|
@ -702,20 +708,18 @@ final class KMKeyboard extends WebView {
|
|||
KMManager.getLatestKeyboardFileVersion(getContext(), packageID, keyboardID) : null;
|
||||
}
|
||||
|
||||
setKeyboardRoot(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 = makeFontPaths(kFont);
|
||||
JSONObject jOskFont = makeFontPaths(kOskFont);
|
||||
|
||||
txtFont = getFontFilename(jDisplayFont);
|
||||
oskFont = getFontFilename(jOskFont);
|
||||
textFontPath = getFontFilename(textFontFilename, packageID);
|
||||
oskFontPath = getFontFilename(oskFontFilename, packageID);
|
||||
|
||||
String kbKey = KMString.format("%s_%s", languageID, keyboardID);
|
||||
|
||||
String keyboardPath = makeKeyboardPath(packageID, keyboardID, keyboardVersion);
|
||||
String keyboardUrl = makeKeyboardUrl(packageID, keyboardID, keyboardVersion);
|
||||
|
||||
JSONObject reg = new JSONObject();
|
||||
try {
|
||||
|
|
@ -723,11 +727,11 @@ final class KMKeyboard extends WebView {
|
|||
reg.put("KI", "Keyboard_" + keyboardID);
|
||||
reg.put("KLC", languageID);
|
||||
reg.put("KL", languageName);
|
||||
reg.put("KF", keyboardPath);
|
||||
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);
|
||||
|
|
@ -784,53 +788,68 @@ 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 setKeyboardRoot(String packageID) {
|
||||
if (packageID.equals(KMManager.KMDefault_UndefinedPackageID)) {
|
||||
this.keyboardRoot = (context.getDir("data", Context.MODE_PRIVATE).toString() +
|
||||
File.separator + KMManager.KMDefault_UndefinedPackageID + File.separator);
|
||||
} else {
|
||||
this.keyboardRoot = (context.getDir("data", Context.MODE_PRIVATE).toString() +
|
||||
File.separator + KMManager.KMDefault_AssetPackages + File.separator + packageID + File.separator);
|
||||
}
|
||||
/**
|
||||
* 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("");
|
||||
}
|
||||
|
||||
public String getKeyboardRoot() {
|
||||
return this.keyboardRoot;
|
||||
/**
|
||||
* 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 makeKeyboardPath(String packageID, String keyboardID, String keyboardVersion) {
|
||||
String keyboardPath;
|
||||
private String getPackageRootUrl(String packageID) {
|
||||
if (packageID.equals(KMManager.KMDefault_UndefinedPackageID)) {
|
||||
keyboardPath = getKeyboardRoot() + keyboardID + "-" + keyboardVersion + ".js";
|
||||
} else {
|
||||
keyboardPath = getKeyboardRoot() + keyboardID + ".js";
|
||||
return getDataRootUrl() + KMManager.KMDefault_UndefinedPackageID + "/";
|
||||
}
|
||||
return keyboardPath;
|
||||
return getDataRootUrl() + KMManager.KMDefault_AssetPackages + "/" + packageID + "/";
|
||||
}
|
||||
|
||||
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 = getPackageRootUrl(packageID);
|
||||
if (packageID.equals(KMManager.KMDefault_UndefinedPackageID)) {
|
||||
keyboardUrl += keyboardID + "-" + keyboardVersion + ".js";
|
||||
} else {
|
||||
keyboardUrl += keyboardID + ".js";
|
||||
}
|
||||
return keyboardUrl;
|
||||
}
|
||||
|
||||
private void sendKMWError(int lineNumber, String sourceId, String message) {
|
||||
|
|
@ -912,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")
|
||||
|
|
@ -1042,13 +1042,28 @@ final class KMKeyboard extends WebView {
|
|||
}
|
||||
|
||||
/**
|
||||
* Take a font JSON object and adjust to pass to JS
|
||||
* 1. Replace "source" keys for "files" keys
|
||||
* 2. Create full font paths for .ttf or .svg
|
||||
* @param font String font JSON object as a string
|
||||
* @return JSONObject of modified font information with full paths. If font is invalid, return `null`
|
||||
* Create a JSON object consisting of the font family and the URLs of the
|
||||
* font files on the local device.
|
||||
*
|
||||
* The `font` parameter can either be the filename of the font (with an
|
||||
* extension recognized as font), or a Font object or JSON string.
|
||||
* In the former case a new JSON object is created with the font family
|
||||
* derived from the filename, and the font filename prefixed with path
|
||||
* to the fonts.
|
||||
* In the latter case the legacy `sources` key is renamed to `files`.
|
||||
* If `files` is a single string it will be prefixed with the path to the
|
||||
* fonts. If `files` is an array, the array is iterated until finding
|
||||
* the first file with a font extension which is then prefixed with the
|
||||
* path to the fonts.
|
||||
*
|
||||
* @param 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 makeFontPaths(String font) {
|
||||
private JSONObject makeFontObject(String font, String packageID) {
|
||||
|
||||
if(font == null || font.equals("")) {
|
||||
return null;
|
||||
|
|
@ -1059,14 +1074,17 @@ final class KMKeyboard extends WebView {
|
|||
JSONObject jfont = new JSONObject();
|
||||
jfont.put(KMManager.KMKey_FontFamily, font.substring(0, font.length()-4));
|
||||
JSONArray jfiles = new JSONArray();
|
||||
jfiles.put(keyboardRoot + font);
|
||||
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);
|
||||
JSONArray sourceArray;
|
||||
String fontFile;
|
||||
|
||||
// Replace "sources" key with "files"
|
||||
if (fontObj.has(KMManager.KMKey_FontSource)) {
|
||||
|
|
@ -1076,16 +1094,18 @@ final class KMKeyboard extends WebView {
|
|||
|
||||
Object obj = fontObj.get(KMManager.KMKey_FontFiles);
|
||||
if (obj instanceof String) {
|
||||
fontFile = fontObj.getString(KMManager.KMKey_FontFiles);
|
||||
fontObj.put(KMManager.KMKey_FontFiles, keyboardRoot + obj);
|
||||
String fontFile = fontObj.getString(KMManager.KMKey_FontFiles);
|
||||
String fontRoot = KMManager.isDefaultFont(fontFile) ? getDataRootUrl() : getPackageRootUrl(packageID);
|
||||
fontObj.put(KMManager.KMKey_FontFiles, fontRoot + obj);
|
||||
return fontObj;
|
||||
} else if (obj instanceof JSONArray) {
|
||||
sourceArray = fontObj.optJSONArray(KMManager.KMKey_FontFiles);
|
||||
JSONArray sourceArray = fontObj.optJSONArray(KMManager.KMKey_FontFiles);
|
||||
if (sourceArray != null) {
|
||||
for (int i = 0; i < sourceArray.length(); i++) {
|
||||
fontFile = sourceArray.getString(i);
|
||||
String fontFile = sourceArray.getString(i);
|
||||
if (FileUtils.hasFontExtension(fontFile)) {
|
||||
fontObj.put(KMManager.KMKey_FontFiles, keyboardRoot + fontFile);
|
||||
String fontRoot = KMManager.isDefaultFont(fontFile) ? getDataRootUrl() : getPackageRootUrl(packageID);
|
||||
fontObj.put(KMManager.KMKey_FontFiles, fontRoot + fontFile);
|
||||
fontObj.remove(KMManager.KMKey_FontSource);
|
||||
return fontObj;
|
||||
}
|
||||
|
|
@ -1094,7 +1114,6 @@ final class KMKeyboard extends WebView {
|
|||
}
|
||||
} catch (JSONException e) {
|
||||
KMLog.LogException(TAG, "Failed to make font for '"+font+"'", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Copyright (C) 2023 SIL International. All rights reserved.
|
||||
/*
|
||||
* Keyman is copyright (C) SIL Global. MIT License.
|
||||
*/
|
||||
package com.keyman.engine;
|
||||
|
||||
|
|
@ -9,9 +9,13 @@ import android.graphics.Bitmap;
|
|||
import android.graphics.RectF;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebResourceRequest;
|
||||
import android.webkit.WebResourceResponse;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.RelativeLayout;
|
||||
import androidx.webkit.WebViewAssetLoader;
|
||||
import androidx.webkit.WebViewAssetLoader.InternalStoragePathHandler;
|
||||
|
||||
import com.keyman.engine.KeyboardEventHandler.EventType;
|
||||
import com.keyman.engine.KMManager;
|
||||
|
|
@ -19,6 +23,7 @@ import com.keyman.engine.KMManager.KeyboardType;
|
|||
import com.keyman.engine.KMManager.SuggestionType;
|
||||
import com.keyman.engine.util.KMLog;
|
||||
import com.keyman.engine.data.Keyboard;
|
||||
import com.keyman.engine.util.WebViewUtils;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
|
@ -31,11 +36,16 @@ public final class KMKeyboardWebViewClient extends WebViewClient {
|
|||
public Context context;
|
||||
private KeyboardType keyboardType;
|
||||
private boolean keyboardLoaded;
|
||||
private WebViewAssetLoader assetLoader;
|
||||
|
||||
KMKeyboardWebViewClient(Context context, KeyboardType keyboardType) {
|
||||
this.context = context;
|
||||
this.keyboardType = keyboardType;
|
||||
this.keyboardLoaded = false;
|
||||
this.assetLoader = new WebViewAssetLoader.Builder()
|
||||
.addPathHandler(WebViewUtils.ASSET_DATA_PATH,
|
||||
new InternalStoragePathHandler(context, context.getDir("data", Context.MODE_PRIVATE)))
|
||||
.build();
|
||||
|
||||
if (keyboardType != KeyboardType.KEYBOARD_TYPE_INAPP && keyboardType != KeyboardType.KEYBOARD_TYPE_SYSTEM) {
|
||||
KMLog.LogError(TAG, String.format("Cannot initialize: Invalid keyboard type: %s", keyboardType.toString()));
|
||||
|
|
@ -58,6 +68,11 @@ public final class KMKeyboardWebViewClient extends WebViewClient {
|
|||
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
|
||||
return this.assetLoader.shouldInterceptRequest(request.getUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url) {
|
||||
Log.d("KMEA", String.format("onPageFinished: [%s] %s", keyboardType.toString(), url));
|
||||
|
|
@ -74,46 +89,44 @@ public final class KMKeyboardWebViewClient extends WebViewClient {
|
|||
kmKeyboard.keyboardSet = false;
|
||||
KMManager.currentLexicalModel = null;
|
||||
|
||||
if (url.startsWith("file")) { // TODO: is this test necessary?
|
||||
this.keyboardLoaded = true;
|
||||
this.keyboardLoaded = true;
|
||||
|
||||
SharedPreferences prefs = context.getSharedPreferences(context.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE);
|
||||
int index = prefs.getInt(KMManager.KMKey_UserKeyboardIndex, 0);
|
||||
if (index < 0) {
|
||||
index = 0;
|
||||
SharedPreferences prefs = context.getSharedPreferences(context.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE);
|
||||
int index = prefs.getInt(KMManager.KMKey_UserKeyboardIndex, 0);
|
||||
Keyboard keyboardInfo = null;
|
||||
if (index >= 0) {
|
||||
keyboardInfo = KMManager.getKeyboardInfo(context, index);
|
||||
}
|
||||
String langId = null;
|
||||
if (keyboardInfo != null) {
|
||||
langId = keyboardInfo.getLanguageID();
|
||||
kmKeyboard.setKeyboard(keyboardInfo);
|
||||
} else {
|
||||
// Revert to default (index 0) or fallback keyboard
|
||||
keyboardInfo = KMManager.getKeyboardInfo(context, 0);
|
||||
if (keyboardInfo == null) {
|
||||
// Don't log to Sentry because some keyboard apps like FV don't install keyboards until the user chooses
|
||||
keyboardInfo = KMManager.getDefaultKeyboard(context);
|
||||
}
|
||||
Keyboard keyboardInfo = KMManager.getKeyboardInfo(context, index);
|
||||
String langId = null;
|
||||
if (keyboardInfo != null) {
|
||||
langId = keyboardInfo.getLanguageID();
|
||||
kmKeyboard.setKeyboard(keyboardInfo);
|
||||
} else {
|
||||
// Revert to default (index 0) or fallback keyboard
|
||||
keyboardInfo = KMManager.getKeyboardInfo(context, 0);
|
||||
if (keyboardInfo == null) {
|
||||
// Don't log to Sentry because some keyboard apps like FV don't install keyboards until the user chooses
|
||||
keyboardInfo = KMManager.getDefaultKeyboard(context);
|
||||
}
|
||||
if (keyboardInfo != null) {
|
||||
langId = keyboardInfo.getLanguageID();
|
||||
kmKeyboard.setKeyboard(keyboardInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KMManager.registerAssociatedLexicalModel(langId);
|
||||
KMManager.registerAssociatedLexicalModel(langId);
|
||||
|
||||
kmKeyboard.showHelpBubbleAfterDelay(2000, true); // check if it should be shown at that time!
|
||||
kmKeyboard.showHelpBubbleAfterDelay(2000, true); // check if it should be shown at that time!
|
||||
|
||||
kmKeyboard.callJavascriptAfterLoad();
|
||||
kmKeyboard.setSpacebarText(KMManager.getSpacebarText());
|
||||
kmKeyboard.callJavascriptAfterLoad();
|
||||
kmKeyboard.setSpacebarText(KMManager.getSpacebarText());
|
||||
|
||||
KeyboardEventHandler.notifyListeners(KMTextView.kbEventListeners, keyboardType, EventType.KEYBOARD_LOADED, null);
|
||||
KeyboardEventHandler.notifyListeners(KMTextView.kbEventListeners, keyboardType, EventType.KEYBOARD_LOADED, null);
|
||||
|
||||
// Special handling for in-app TextView context keymanapp/keyman#3809
|
||||
if (keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP &&
|
||||
KMTextView.activeView != null && KMTextView.activeView.getClass() == KMTextView.class) {
|
||||
KMTextView.updateTextContext();
|
||||
}
|
||||
// Special handling for in-app TextView context keymanapp/keyman#3809
|
||||
if (keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP &&
|
||||
KMTextView.activeView != null && KMTextView.activeView.getClass() == KMTextView.class) {
|
||||
KMTextView.updateTextContext();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Copyright (C) 2017 SIL International. All rights reserved.
|
||||
/*
|
||||
* Keyman is copyright (C) SIL Global. MIT License.
|
||||
*/
|
||||
|
||||
package com.keyman.engine;
|
||||
|
|
@ -406,6 +406,10 @@ public final class KMManager {
|
|||
return getResourceRoot() + KMDefault_LexicalModelPackages + File.separator;
|
||||
}
|
||||
|
||||
public static String getLexicalModelsUrl() {
|
||||
return WebViewUtils.buildAssetUrl(KMDefault_LexicalModelPackages + "/");
|
||||
}
|
||||
|
||||
public static String getCloudDir() {
|
||||
return getResourceRoot() + KMDefault_UndefinedPackageID + File.separator;
|
||||
}
|
||||
|
|
@ -496,6 +500,19 @@ public final class KMManager {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given font is the default font or the default OSK font.
|
||||
* @param fontName String - The font name and extension
|
||||
* @return boolean true if `fontName` is DejaVuSans.ttf or keymanweb-osk.ttf
|
||||
* or null or empty string, false otherwise
|
||||
*/
|
||||
public static boolean isDefaultFont(String fontName) {
|
||||
if (fontName == null || fontName.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
return fontName.equals(KMDefault_KeyboardFont) || fontName.equals(KMFilename_Osk_Ttf_Font);
|
||||
}
|
||||
|
||||
public static void initialize(final Context context, KeyboardType keyboardType) {
|
||||
appContext = context.getApplicationContext();
|
||||
|
||||
|
|
@ -1612,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) &&
|
||||
|
|
@ -1650,8 +1677,9 @@ public final class KMManager {
|
|||
String modelID = lexicalModelInfo.get(KMKey_LexicalModelID);
|
||||
String languageID = lexicalModelInfo.get(KMKey_LanguageID);
|
||||
boolean modelFileExists = true;
|
||||
File modelFile = new File(getLexicalModelsDir(), pkgID + File.separator + modelID + ".model.js");
|
||||
String path = "file://" + modelFile.getAbsolutePath();
|
||||
String modelFilename = pkgID + File.separator + modelID + ".model.js";
|
||||
File modelFile = new File(getLexicalModelsDir(), modelFilename);
|
||||
String url = getLexicalModelsUrl() + modelFilename;
|
||||
|
||||
// Disable sugestions if lexical-model file doesn't exist
|
||||
if (!modelFile.exists()) {
|
||||
|
|
@ -1666,7 +1694,7 @@ public final class KMManager {
|
|||
modelObj.put("id", modelID);
|
||||
languageJSONArray.put(languageID);
|
||||
modelObj.put("languages", languageJSONArray);
|
||||
modelObj.put("path", path);
|
||||
modelObj.put("path", url);
|
||||
modelObj.put("CustomHelpLink", lexicalModelInfo.get(KMKey_CustomHelpLink));
|
||||
} catch (JSONException e) {
|
||||
KMLog.LogException(TAG, "Invalid lexical model to register", e);
|
||||
|
|
@ -2225,7 +2253,12 @@ public final class KMManager {
|
|||
File kmpJSONFile = new File(path);
|
||||
if (!kmpJSONFile.exists()) {
|
||||
if (!KMManager.isTestMode()) {
|
||||
KMLog.LogError(TAG, path + " not found. Returning version 1.0");
|
||||
if (packageID.equals(KMDefault_PackageID)) {
|
||||
// Suppress error log for the default fallback keyboard if it's not installed
|
||||
Log.d(TAG, path + " not found. Returning version 1.0");
|
||||
} else {
|
||||
KMLog.LogError(TAG, path + " not found. Returning version 1.0");
|
||||
}
|
||||
}
|
||||
return "1.0";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Copyright (C) 2020 SIL International. All rights reserved.
|
||||
/*
|
||||
* Keyman is copyright (C) SIL Global. MIT License.
|
||||
*/
|
||||
package com.keyman.engine.data;
|
||||
|
||||
|
|
@ -15,6 +15,7 @@ import com.keyman.engine.util.BCP47;
|
|||
import com.keyman.engine.util.FileUtils;
|
||||
import com.keyman.engine.util.KMLog;
|
||||
import com.keyman.engine.util.KMString;
|
||||
import com.keyman.engine.util.WebViewUtils;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
|
@ -48,6 +49,7 @@ public class Keyboard extends LanguageResource implements Serializable {
|
|||
*/
|
||||
public Keyboard(JSONObject installedObj) {
|
||||
this.fromJSON(installedObj);
|
||||
logIfLegacyKeyboard();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -78,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);
|
||||
}
|
||||
|
|
@ -95,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) {
|
||||
|
|
@ -107,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(); }
|
||||
|
||||
|
|
@ -173,24 +185,24 @@ public class Keyboard extends LanguageResource implements Serializable {
|
|||
return o;
|
||||
}
|
||||
|
||||
private String getKeyboardRoot(Context context) {
|
||||
String keyboardRoot = context.getDir("data", Context.MODE_PRIVATE).toString() +
|
||||
File.separator;
|
||||
|
||||
if (packageID.equals(KMManager.KMDefault_UndefinedPackageID)) {
|
||||
return keyboardRoot + KMManager.KMDefault_UndefinedPackageID + File.separator;
|
||||
} else {
|
||||
return keyboardRoot + KMManager.KMDefault_AssetPackages + File.separator + packageID + File.separator;
|
||||
}
|
||||
private String getDataRoot() {
|
||||
return WebViewUtils.buildAssetUrl("");
|
||||
}
|
||||
|
||||
public String getKeyboardPath(Context context) {
|
||||
private String getPackageRoot() {
|
||||
if (packageID.equals(KMManager.KMDefault_UndefinedPackageID)) {
|
||||
return getDataRoot() + KMManager.KMDefault_UndefinedPackageID + "/";
|
||||
}
|
||||
return getDataRoot() + KMManager.KMDefault_AssetPackages + "/" + packageID + "/";
|
||||
}
|
||||
|
||||
private String getKeyboardUrl() {
|
||||
String keyboardID = this.getKeyboardID();
|
||||
String keyboardVersion = this.getVersion();
|
||||
if (packageID.equals(KMManager.KMDefault_UndefinedPackageID)) {
|
||||
return getKeyboardRoot(context) + keyboardID + "-" + keyboardVersion + ".js";
|
||||
return getPackageRoot() + keyboardID + "-" + keyboardVersion + ".js";
|
||||
} else {
|
||||
return getKeyboardRoot(context) + keyboardID + ".js";
|
||||
return getPackageRoot() + keyboardID + ".js";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -202,17 +214,17 @@ public class Keyboard extends LanguageResource implements Serializable {
|
|||
stubObj.put("KI", "Keyboard_" + this.getKeyboardID());
|
||||
stubObj.put("KLC", this.getLanguageID());
|
||||
stubObj.put("KL", this.getLanguageName());
|
||||
stubObj.put("KF", this.getKeyboardPath(context));
|
||||
stubObj.put("KF", this.getKeyboardUrl());
|
||||
stubObj.put("KP", this.getPackageID());
|
||||
|
||||
String displayFont = this.getFont();
|
||||
if(displayFont != null) {
|
||||
stubObj.put("KFont", this.buildDisplayFontObject(displayFont, context));
|
||||
stubObj.put("KFont", this.buildDisplayFontObject(displayFont));
|
||||
}
|
||||
|
||||
String oskFont = this.getOSKFont();
|
||||
if(oskFont != null) {
|
||||
stubObj.put("KOskFont", this.buildDisplayFontObject(oskFont, context));
|
||||
stubObj.put("KOskFont", this.buildDisplayFontObject(oskFont));
|
||||
}
|
||||
|
||||
String displayName = this.getDisplayName();
|
||||
|
|
@ -228,34 +240,33 @@ public class Keyboard extends LanguageResource implements Serializable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Take a font JSON object and adjust to pass to JS
|
||||
* 1. Replace "source" keys for "files" keys
|
||||
* 2. Create full font paths for .ttf or .svg
|
||||
* @param font String font JSON object as a string
|
||||
* @return JSONObject of modified font information with full paths. If font is invalid, return `null`
|
||||
* Create a JSON object consisting of the font family and the font
|
||||
* file URL.
|
||||
*
|
||||
* @param font A string containing the font filename
|
||||
* @return JSONObject of modified font information with full URL. If font
|
||||
* is invalid, return `null`.
|
||||
*/
|
||||
private JSONObject buildDisplayFontObject(String font, Context context) {
|
||||
private JSONObject buildDisplayFontObject(String font) {
|
||||
if(font == null || font.equals("")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String keyboardRoot = this.getKeyboardRoot(context);
|
||||
String fontRoot = KMManager.isDefaultFont(font) ? this.getDataRoot() : this.getPackageRoot();
|
||||
|
||||
try {
|
||||
if (FileUtils.hasFontExtension(font)) {
|
||||
JSONObject jfont = new JSONObject();
|
||||
jfont.put(KMManager.KMKey_FontFamily, font.substring(0, font.length() - 4));
|
||||
JSONArray jfiles = new JSONArray();
|
||||
jfiles.put(keyboardRoot + font);
|
||||
jfiles.put(fontRoot + font);
|
||||
jfont.put(KMManager.KMKey_FontFiles, jfiles);
|
||||
return jfont;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
KMLog.LogException(TAG, "Failed to make font for '"+font+"'", e);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,28 @@ public final class WebViewUtils {
|
|||
private static final String CHROME_INSTALL_PATTERN_FORMATSTR = "^.*Chrome/([\\d.]+).*$";
|
||||
private static final Pattern installPattern = Pattern.compile(CHROME_INSTALL_PATTERN_FORMATSTR);
|
||||
|
||||
/**
|
||||
* Reserved magic domain for loading files from the local device. At runtime
|
||||
* the WebViewAssetLoader will replace the protocol and domain with the
|
||||
* internal storage path.
|
||||
* See https://developer.android.com/reference/androidx/webkit/WebViewAssetLoader
|
||||
*/
|
||||
private static final String MAGIC_DEFAULT_DOMAIN = "https://appassets.androidplatform.net";
|
||||
|
||||
/**
|
||||
* Path under the asset domain where all assets live
|
||||
*/
|
||||
public static final String ASSET_DATA_PATH = "/data/";
|
||||
|
||||
/**
|
||||
* Build a full URL to the provided asset
|
||||
*/
|
||||
public static String buildAssetUrl(String assetPath) {
|
||||
String appendAsset = assetPath == null ? "" :
|
||||
(assetPath.startsWith("/") ? assetPath.substring(1) : assetPath);
|
||||
return WebViewUtils.MAGIC_DEFAULT_DOMAIN + WebViewUtils.ASSET_DATA_PATH + appendAsset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Keyman Engine mode based on the Chrome version.
|
||||
* @param context - The context
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 -->
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 -->
|
||||
|
|
|
|||
|
|
@ -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 -->
|
||||
|
|
|
|||
|
|
@ -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 -->
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 -->
|
||||
|
|
|
|||
|
|
@ -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 -->
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 -->
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 -->
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 -->
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
/*
|
||||
* Keyman is copyright (C) SIL Global. MIT License.
|
||||
*/
|
||||
package com.keyman.engine;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
|
|
@ -32,7 +36,11 @@ public class KMManagerTest {
|
|||
// For some keyboard list tests, load an existing keyboard list.
|
||||
// Can't use @Before because context is null before running tests.
|
||||
public void loadOldKeyboardsList() {
|
||||
KMManager.initialize(ApplicationProvider.getApplicationContext(), KMManager.KeyboardType.KEYBOARD_TYPE_INAPP);
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
// Create appData directory accessed in KMKeyboardWebViewClient
|
||||
File dataDir = context.getDir("data", Context.MODE_PRIVATE);
|
||||
dataDir.mkdirs();
|
||||
KMManager.initialize(context, KMManager.KeyboardType.KEYBOARD_TYPE_INAPP);
|
||||
|
||||
File keyboards_dat = new File(TEST_RESOURCE_ROOT, OLD_KEYBOARDS_LIST);
|
||||
if (keyboards_dat == null || !keyboards_dat.exists()) {
|
||||
|
|
@ -111,6 +119,11 @@ public class KMManagerTest {
|
|||
*/
|
||||
@Test
|
||||
public void create_newKeyboardsList() {
|
||||
if(File.separatorChar != '/') {
|
||||
Log.w(TAG, "create_newKeyboardsList skipped due to #16183");
|
||||
return;
|
||||
}
|
||||
|
||||
loadOldKeyboardsList();
|
||||
dat_list = new ArrayList<HashMap<String, String>>();
|
||||
|
||||
|
|
@ -182,6 +195,12 @@ public class KMManagerTest {
|
|||
|
||||
@Test
|
||||
public void test_updateOldKeyboardsList() {
|
||||
// #16183
|
||||
if(File.separatorChar != '/') {
|
||||
Log.w(TAG, "test_updateOldKeyboardsList skipped due to #16183");
|
||||
return;
|
||||
}
|
||||
|
||||
loadOldKeyboardsList();
|
||||
Assert.assertNotNull(dat_list);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -26,37 +26,48 @@ public class WebViewUtilsTest {
|
|||
@Test
|
||||
public void test_ChromeEmpty_EngineWebViewVersionStatusDisabled() {
|
||||
String chromeVersion = "";
|
||||
Assert.assertEquals(WebViewUtils.getEngineWebViewVersionStatus(context, null, chromeVersion),
|
||||
WebViewUtils.EngineWebViewVersionStatus.DISABLED);
|
||||
Assert.assertEquals(WebViewUtils.EngineWebViewVersionStatus.DISABLED,
|
||||
WebViewUtils.getEngineWebViewVersionStatus(context, null, chromeVersion));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Chrome36_EngineWebViewVersionStatusDisabled() {
|
||||
double chromeVersionFloat = Float.parseFloat(WebViewUtils.KEYMAN_MIN_TARGET_VERSION_DEGRADED_ANDROID_CHROME) - 1.0;
|
||||
String chromeVersion = String.valueOf(chromeVersionFloat);
|
||||
Assert.assertEquals(WebViewUtils.getEngineWebViewVersionStatus(context, null, chromeVersion),
|
||||
WebViewUtils.EngineWebViewVersionStatus.DISABLED);
|
||||
Assert.assertEquals(WebViewUtils.EngineWebViewVersionStatus.DISABLED,
|
||||
WebViewUtils.getEngineWebViewVersionStatus(context, null, chromeVersion));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Chrome37_EngineWebViewVersionStatusDegraded() {
|
||||
String chromeVersion = WebViewUtils.KEYMAN_MIN_TARGET_VERSION_DEGRADED_ANDROID_CHROME;
|
||||
Assert.assertEquals(WebViewUtils.getEngineWebViewVersionStatus(context, null, chromeVersion),
|
||||
WebViewUtils.EngineWebViewVersionStatus.DEGRADED);
|
||||
Assert.assertEquals(WebViewUtils.EngineWebViewVersionStatus.DEGRADED,
|
||||
WebViewUtils.getEngineWebViewVersionStatus(context, null, chromeVersion));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Chrome94_EngineWebViewVersionStatusDegraded() {
|
||||
double chromeVersionFloat = Float.parseFloat(WebViewUtils.KEYMAN_MIN_TARGET_VERSION_ANDROID_CHROME) - 1.0;
|
||||
String chromeVersion = String.valueOf(chromeVersionFloat);
|
||||
Assert.assertEquals(WebViewUtils.getEngineWebViewVersionStatus(context, null, chromeVersion),
|
||||
WebViewUtils.EngineWebViewVersionStatus.DEGRADED);
|
||||
Assert.assertEquals(WebViewUtils.EngineWebViewVersionStatus.DEGRADED,
|
||||
WebViewUtils.getEngineWebViewVersionStatus(context, null, chromeVersion));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Chrome95_EngineWebViewVersionStatusFull() {
|
||||
String chromeVersion = WebViewUtils.KEYMAN_MIN_TARGET_VERSION_ANDROID_CHROME;
|
||||
Assert.assertEquals(WebViewUtils.getEngineWebViewVersionStatus(context, null, chromeVersion),
|
||||
WebViewUtils.EngineWebViewVersionStatus.FULL);
|
||||
Assert.assertEquals(WebViewUtils.EngineWebViewVersionStatus.FULL,
|
||||
WebViewUtils.getEngineWebViewVersionStatus(context, null, chromeVersion));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_buildAssetUrl() {
|
||||
Assert.assertEquals("https://appassets.androidplatform.net/data/", WebViewUtils.buildAssetUrl(""));
|
||||
Assert.assertEquals("https://appassets.androidplatform.net/data/", WebViewUtils.buildAssetUrl(null));
|
||||
Assert.assertEquals("https://appassets.androidplatform.net/data/foo", WebViewUtils.buildAssetUrl("foo"));
|
||||
Assert.assertEquals("https://appassets.androidplatform.net/data/foo/", WebViewUtils.buildAssetUrl("foo/"));
|
||||
Assert.assertEquals("https://appassets.androidplatform.net/data/foo/", WebViewUtils.buildAssetUrl("/foo/"));
|
||||
Assert.assertEquals("https://appassets.androidplatform.net/data/foo/bar.html", WebViewUtils.buildAssetUrl("foo/bar.html"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
@ -50,4 +61,5 @@ dependencies {
|
|||
implementation 'com.google.android.material:material:1.12.0'
|
||||
implementation files('libs/keyman-engine.aar')
|
||||
implementation 'androidx.preference:preference:1.2.1'
|
||||
implementation 'androidx.webkit:webkit:1.14.0'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
|
|
@ -17,7 +16,6 @@ buildscript {
|
|||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -49,4 +61,5 @@ dependencies {
|
|||
implementation 'com.google.android.material:material:1.12.0'
|
||||
implementation files('libs/keyman-engine.aar')
|
||||
implementation 'androidx.preference:preference:1.2.1'
|
||||
implementation 'androidx.webkit:webkit:1.14.0'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
|
|
@ -17,7 +16,6 @@ buildscript {
|
|||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
@ -60,4 +61,5 @@ dependencies {
|
|||
implementation 'com.google.android.material:material:1.12.0'
|
||||
implementation files('libs/keyman-engine.aar')
|
||||
implementation 'androidx.preference:preference:1.2.1'
|
||||
implementation 'androidx.webkit:webkit:1.14.0'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
|
|||
KMManager.KMDefault_KeyboardFont,
|
||||
KMManager.KMDefault_KeyboardFont);
|
||||
KMManager.addKeyboard(this, chiralityKBInfo);
|
||||
Keyboard.setDefaultKeyboard(chiralityKBInfo);
|
||||
|
||||
// Longpress test keyboard
|
||||
Keyboard longpressKBbInfo = new Keyboard(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
20
android/android-utils.inc.sh
Normal file
20
android/android-utils.inc.sh
Normal 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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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`,
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Reference in a new issue