Merge branch 'epic/win-arm' into feat/windows/11905/keymanx64-keymanarm64

This commit is contained in:
rc-swag 2025-10-01 14:11:23 +10:00
commit 5ac167977b
681 changed files with 19854 additions and 8717 deletions

1
.gitattributes vendored
View file

@ -7,3 +7,4 @@
# EOF
# ```
Dockerfile text eol=lf
*.sh text eol=lf

View file

@ -23,6 +23,7 @@ jobs:
GIT_BRANCH: ${{ steps.environment_step.outputs.GIT_BRANCH }}
GIT_BASE_BRANCH: ${{ steps.environment_step.outputs.GIT_BASE_BRANCH }}
GIT_USER: ${{ steps.environment_step.outputs.GIT_USER }}
SKIP_API_CHECK: ${{ steps.environment_step.outputs.SKIP_API_CHECK }}
steps:
- name: Restore artifacts
@ -40,7 +41,7 @@ jobs:
- name: Set pending status on PR builds
id: set_status
if: steps.environment_step.outputs.IS_TEST_BUILD == 'true'
if: steps.environment_step.outputs.IS_TEST_BUILD == 'true' && steps.environment_step.outputs.SKIP_API_CHECK != 'true'
shell: bash
run: |
gh api \
@ -53,6 +54,7 @@ jobs:
-f context="$STATUS_CONTEXT"
- name: Checkout
if: steps.environment_step.outputs.SKIP_API_CHECK != 'true'
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
with:
ref: '${{ steps.environment_step.outputs.GIT_SHA }}'
@ -61,11 +63,13 @@ jobs:
path: ${{ github.workspace }}/keyman/
- name: Install devscripts
if: steps.environment_step.outputs.SKIP_API_CHECK != 'true'
uses: ./keyman/.github/actions/apt-install
with:
packages: devscripts equivs
- 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
@ -79,17 +83,17 @@ jobs:
verify 2>> $GITHUB_STEP_SUMMARY
- name: Archive .symbols file
if: steps.environment_step.outputs.SKIP_API_CHECK != 'true' && always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: libkeymancore.symbols
path: ${{ github.workspace }}/keyman/linux/debian/tmp/DEBIAN/symbols
if: always()
set_status:
name: Set result status on PR builds
needs: [api_verification]
runs-on: ubuntu-latest
if: ${{ always() && needs.api_verification.outputs.IS_TEST_BUILD == 'true' }}
if: ${{ always() && needs.api_verification.outputs.IS_TEST_BUILD == 'true' && needs.api_verification.outputs.SKIP_API_CHECK != 'true' }}
steps:
- name: Set success
if: needs.api_verification.result == 'success'

View file

@ -1,10 +1,9 @@
#
# Automatically merges pull requests opened by keyman-server
# The initial use of this action is to increment the patch
# version of Keyman with resources/build/increment-version.sh.
# That script creates a PR and then this workflow will be
# triggered to approve the PR (from github-actions account), and
# then automerge it.
# Automatically merges pull requests opened by keyman-server The initial use of
# this action is to increment the patch version of Keyman with
# resources/teamcity/triggers/trigger-release-builds.sh. That script creates a PR and
# then this workflow will be triggered to approve the PR (from github-actions
# account), and then automerge it.
#
name: Auto Merge PRs from keyman-server
on:
@ -17,12 +16,15 @@ jobs:
if: ${{ github.repository == 'keymanapp/keyman' && github.actor == 'keyman-server' && startsWith(github.event.pull_request.title, 'auto:') }}
steps:
- name: auto approve PR from keyman-server
uses: hmarr/auto-approve-action@f0939ea97e9205ef24d872e76833fa908a770363
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: auto merge PR from keyman-server
uses: "pascalgn/automerge-action@7854d3bd607dccdaf0b2c134b699a812c8960213"
shell: bash
env:
GITHUB_TOKEN: "${{ secrets.AUTOINC_GITHUB_TOKEN }}"
MERGE_LABELS: "automerge"
MERGE_FORKS: false
GH_TOKEN: "${{ github.token }}"
run: |
gh pr review -R keymanapp/keyman --approve ${{ github.event.pull_request.number }}
- name: mark PR for auto-merge from keyman-server
shell: bash
if: contains(github.event.pull_request.labels.*.name, 'automerge')
env:
GH_TOKEN: "${{ secrets.AUTOINC_GITHUB_TOKEN }}"
run: |
gh pr merge -R keymanapp/keyman --merge --auto ${{ github.event.pull_request.number }}

View file

@ -0,0 +1,79 @@
# This workflow runs the build, test and publish steps for the
# Keyman build Docker images.
# It is triggered by a schedule and repository_dispatch event.
name: "Docker images: Build, publish and test "
run-name: "Docker images: Build, publish and test (branch ${{ github.ref_name }} on ${{ github.event_name }}/${{ github.event.action }} by ${{ github.actor }})"
defaults:
run:
shell: bash
on:
schedule:
#run every Fri, 3 pm UTC
- cron: '0 15 * * 5'
repository_dispatch:
types: ['build-test-publish-docker:*']
env:
REGISTRY: ghcr.io
USERNAME: "${{ github.actor }}"
PASSWORD: "${{ secrets.KEYMAN_SERVER_REPO_ACCESS_PAT }}"
jobs:
message:
name: Print message
runs-on: ubuntu-latest
if: ${{ github.event.act }}
steps:
- name: Print message
id: print-message
run: |
echo "Docker images: Build, publish and test (branch ${{ github.ref_name }} on ${{ github.event_name }}/${{ github.event.action }} by ${{ github.actor }})"
build:
name: Build Docker images
runs-on: ubuntu-latest
steps:
- name: Checkout repository
id: checkout
uses: actions/checkout@v4
- name: Build Docker images
id: build
run: |
if [ -z "${{ env.USERNAME }}" ]; then
resources/docker-images/build.sh build: --registry ${{ env.REGISTRY }}
else
echo ${{ env.PASSWORD }} | resources/docker-images/build.sh build: --registry ${{ env.REGISTRY }} --username ${{ env.USERNAME }}
fi
- name: Publish Docker images
id: publish
run: |
if [ -z "${{ env.USERNAME }}" ]; then
resources/docker-images/build.sh publish: --registry ${{ env.REGISTRY }}
else
echo ${{ env.PASSWORD }} | resources/docker-images/build.sh publish: --registry ${{ env.REGISTRY }} --username ${{ env.USERNAME }}
fi
test:
name: Test Docker images
runs-on: ubuntu-latest
needs: build
steps:
- name: Checkout repository
id: checkout
uses: actions/checkout@v4
- name: Test Docker images
id: test
run: |
if [ -z "${{ env.USERNAME }}" ]; then
resources/docker-images/build.sh test: --registry ${{ env.REGISTRY }}
else
echo ${{ env.PASSWORD }} | resources/docker-images/build.sh test: --registry ${{ env.REGISTRY }} --username ${{ env.USERNAME }}
fi

45
.github/workflows/cleanup-ghcr.yml vendored Normal file
View file

@ -0,0 +1,45 @@
# This workflow removes all untagged Docker images from the
# GitHub Container Registry.
# It is triggered by a schedule event or a manual workflow dispatch event.
name: "Docker images: Cleanup GitHub Container Registry"
run-name: "Docker images: Cleanup GitHub Container Registry (branch ${{ github.ref_name }} on ${{ github.event_name }} by ${{ github.actor }})"
defaults:
run:
shell: bash
on:
schedule:
#run every Fri, 6 pm UTC
- cron: '0 18 * * 5' #
workflow_dispatch:
jobs:
message:
name: Print message
runs-on: ubuntu-latest
if: ${{ github.event.act }}
steps:
- name: Print message
id: print-message
run: |
echo "Docker images: Cleanup GitHub Container Registry (branch ${{ github.ref_name }} on ${{ github.event_name }} by ${{ github.actor }})"
cleanup-images:
name: Cleanup GitHub Container Registry images
runs-on: ubuntu-latest
steps:
- name: Removing all untagged images from GitHub Container Registry
id: cleanup-ghcr
uses: dataaxiom/ghcr-cleanup-action@cd0cdb900b5dbf3a6f2cc869f0dbb0b8211f50c4 # v1.0.16
with:
dry-run: false
expand-packages: true
packages: keyman-*-ci
delete-untagged: true
token: ${{ secrets.KEYMAN_SERVER_REPO_ACCESS_PAT }}

View file

@ -18,7 +18,8 @@ You can manually trigger a deb-packaging action on GitHub, e.g. to test changes:
\"baseBranch\": \"master\",
\"baseRef\": \"$(git rev-parse refs/heads/master^)\",
\"user\": \"${USER}\",
\"isTestBuild\": \"true\"}" \
\"isTestBuild\": \"true\",
\"force\": \"true\"}" \
https://api.github.com/repos/<yourgithubname>/keyman/dispatches
```
@ -36,6 +37,7 @@ You can manually trigger a deb-packaging action on GitHub, e.g. to test changes:
\"baseBranch\": \"master\",
\"baseRef\": \"$(git rev-parse refs/heads/master)\",
\"user\": \"${USER}\",
\"isTestBuild\": \"true\"}" \
\"isTestBuild\": \"true\",
\"force\": \"true\"}" \
https://api.github.com/repos/<yourgithubname>/keyman/dispatches
```

View file

@ -23,6 +23,7 @@ on:
# baseRef: The ref of the previous commit. For a PR the same as `baseBranch`.
# user: The user that triggered the build or created the PR
# isTestBuild: false for Releases, otherwise true
# skipApiCheck: true to skip the API check, otherwise false
env:
COLOR_GREEN: "\e[32m"
@ -34,7 +35,7 @@ env:
jobs:
sourcepackage:
name: Build source package
if: github.repository == 'keymanapp/keyman'
if: github.repository == 'keymanapp/keyman' || github.event.client_payload.force
runs-on: ubuntu-24.04
outputs:
KEYMAN_VERSION: ${{ steps.version_step.outputs.KEYMAN_VERSION }}
@ -86,7 +87,14 @@ jobs:
shell: bash
run: |
THIS_SCRIPT="$GITHUB_WORKSPACE/.github/workflows/deb-packaging.yml"
. "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh"
if [[ -f "${THIS_SCRIPT%/*}/../../resources/build/builder-basic.inc.sh" ]]; then
. "${THIS_SCRIPT%/*}/../../resources/build/builder-basic.inc.sh"
else
# TODO: remove following block post v19 release (file was renamed in v19 alpha)
if [[ -f "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh" ]]; then
. "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh"
fi
fi
echo "KEYMAN_VERSION=${KEYMAN_VERSION:-${VERSION}}" >> $GITHUB_OUTPUT
- name: Set prerelease tag as output parameter
@ -177,28 +185,36 @@ jobs:
runs-on: ubuntu-latest
steps:
# - name: Download Artifacts
# uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
# with:
# path: artifacts
# merge-multiple: true
- name: Download Source Artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: keyman-srcpkg
path: artifacts
# - name: Install dependencies
# run: |
# sudo DEBIAN_FRONTEND=noninteractive apt-get -q -y install autopkgtest qemu-system qemu-utils autodep8 genisoimage python3-distro-info
- name: Download Binary Artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
path: artifacts
pattern: keyman-binarypkgs-*
merge-multiple: true
# - name: Build test image
# run: |
# cd "${GITHUB_WORKSPACE}/artifacts"
# autopkgtest-buildvm-ubuntu-cloud -v --release=jammy
# - name: Run tests
# run: |
# cd "${GITHUB_WORKSPACE}/artifacts"
# autopkgtest -B *.deb keyman_*.dsc -- qemu autopkgtest-jammy-amd64.img
- name: Ignore
- name: Install dependencies
run: |
echo "Ignored for now - until working solution is in place (#13777)"
sudo apt update
sudo DEBIAN_FRONTEND=noninteractive apt-get -q -y install autopkgtest qemu-system qemu-utils autodep8 genisoimage python3-distro-info
- name: Build test image
run: |
cd "${GITHUB_WORKSPACE}/artifacts"
sudo chown ${USER} /dev/kvm
autopkgtest-buildvm-ubuntu-cloud -v --release=$(lsb_release -c -s)
- name: Run tests
run: |
# Tests are defined in linux/debian/tests/test-build
cd "${GITHUB_WORKSPACE}/artifacts"
DIST="$(lsb_release -c -s)"
autopkgtest -B *${DIST}*.deb keyman_*.dsc -- qemu autopkgtest-${DIST}-amd64.img
deb_signing:
name: Sign source and binary packages
@ -325,6 +341,7 @@ jobs:
echo "GIT_BRANCH=${{ github.event.client_payload.branch }}" >> artifacts/env
echo "GIT_BASE_BRANCH=${{ github.event.client_payload.baseBranch }}" >> artifacts/env
echo "GIT_USER=${{ github.event.client_payload.user }}" >> artifacts/env
echo "SKIP_API_CHECK=${{ github.event.client_payload.skipApiCheck }}" >> artifacts/env
- name: Cache artifacts
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3

View file

@ -55,43 +55,61 @@ jobs:
// check/web/file-size
//
function addLog(message) {
console.log(message);
return message + String.fromCharCode(10);
}
function addStatus(o, type, context, state) {
if(!o[context]) {
o[context] = {type, state};
return addLog(`Found ${context}: ${state}`);
} else {
return addLog(`Skipping repeated ${context}: ${state}`);
}
}
function reduceStatuses(statuses) {
let summary = '';
const filtered_statuses = statuses.reduce((o, status) => {
if(status.creator?.login == 'keyman-server' && status.context.startsWith('Test')) {
if(!o[status.context]) o[status.context] = {type: 'build', state: status.state};
summary += addStatus(o, 'build', status.context, status.state);
} else if(status.creator?.login == 'keymanapp-test-bot[bot]' && status.context == 'user_testing') {
if(!o[status.context]) o[status.context] = {type: 'user-test', state: status.state};;
summary += addStatus(o, 'user-test', status.context, status.state);
} else if(status.context == 'API Verification') {
if(!o[status.context]) o[status.context] = {type: 'check', state: status.state};
summary += addStatus(o, 'check', status.context, status.state);
} else if(status.context == 'Ubuntu Packaging') {
if(!o[status.context]) o[status.context] = {type: 'build', state: status.state};
summary += addStatus(o, 'build', status.context, status.state);
} else if(status.context == 'check/web/file-size') {
// Ignore check/web/file-size -- we won't block automerge for this at this point
summary += addLog(`Skipping ${status.context}`);
} else {
// We fail with an 'unknown status' response if we get a new status check
// so we can be sure we are not skipping known status checks
summary += addLog(`Found unknown ${status.context}: ${status.state}`);
o[status.context] = {type: 'unknown', state: status.state};
}
return o;
}, {});
return filtered_statuses;
return { filtered_statuses, summary };
}
//
// Given the collection of status checks we care about, return
// an aggregate status -- error, failed, pending, or success,
// an aggregate status -- error, failure, pending, or success,
// and a summary description
//
function calculateFinalStatus(filtered_statuses) {
function calculateFinalStatus(filtered_statuses, summary) {
const counts = {};
let hasBuilds = false;
for(const context of Object.keys(filtered_statuses)) {
const { state, type } = filtered_statuses[context];
if(type == 'unknown') {
// We special-case for unknown status checks, and never permit them
console.error(`An unknown context ${context} was found, cannot calculate build status.`);
return [
'error', `An unknown context ${context} was found, cannot calculate build status.`
'error', `An unknown context ${context} was found, cannot calculate build status.`, summary
];
}
if(type == 'build') {
@ -102,12 +120,13 @@ jobs:
// If we do not have any statuses yet, we wait
if(Object.keys(filtered_statuses).length == 0 || !hasBuilds) {
return ['pending', 'Builds have not yet been triggered ⌛'];
console.log(`Builds not yet triggered`);
return ['pending', 'Builds have not yet been triggered ⌛', summary];
}
const state =
counts.error ? 'error' :
counts.failed ? 'failed' :
counts.failure ? 'failure' :
counts.pending ? 'pending' :
'success';
@ -118,14 +137,16 @@ jobs:
description += `${count} check${count == 1 ? '' : 's'} ${state}`;
}
appendDescription(counts.error, 'in an error state ❌');
appendDescription(counts.failed, 'failed ❌');
appendDescription(counts.failure, 'failed ❌');
appendDescription(counts.pending, 'pending ⌛');
appendDescription(counts.success, 'completed successfully ✅');
return [ state, description ];
console.log(`Finished filtering and reviewing status checks: ${state}: ${description}`);
return [ state, description, summary ];
}
async function getCommitStatuses(github, owner, repo, sha) {
console.log(`Getting commit statuses for ${sha}`);
const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/statuses', {
owner,
repo,
@ -138,6 +159,7 @@ jobs:
}
async function getCommitCheckRuns(github, owner, repo, sha) {
console.log(`Getting commit check runs for ${sha}`);
const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/check-runs', {
owner,
repo,
@ -151,13 +173,11 @@ jobs:
function calculateCheckResult(statuses) {
if(!Array.isArray(statuses)) {
return ['error', 'Failed to retrieve status checks from GitHub ❌'];
return ['error', 'Failed to retrieve status checks from GitHub ❌', 'statuses is not an array'];
}
const filtered_statuses = reduceStatuses(statuses);
const result = calculateFinalStatus(filtered_statuses);
return result;
const { filtered_statuses, summary } = reduceStatuses(statuses);
return calculateFinalStatus(filtered_statuses, summary);
}
async function test(github, owner, repo, sha) {
@ -177,7 +197,7 @@ jobs:
return check.data.id;
}
async function updateCheck(github, owner, repo, checkRunId, status, description) {
async function updateCheck(github, owner, repo, checkRunId, status, description, summary) {
const checkStatus = status == 'pending' ? 'in_progress' : 'completed';
const conclusion = checkStatus == 'in_progress' ? undefined : (status == 'success' ? 'success' : 'failure');
@ -189,7 +209,7 @@ jobs:
conclusion,
output: {
title: description,
summary: ''
summary
}
});
}
@ -204,5 +224,5 @@ jobs:
const checkRunId = await createCheck(github, owner, repo, sha);
const res = await test(github, owner, repo, sha);
await updateCheck(github, owner, repo, checkRunId, res[0], res[1]);
await updateCheck(github, owner, repo, checkRunId, res[0], res[1], res[2]);

6
.gitignore vendored
View file

@ -152,8 +152,8 @@
# Atom/Nuclide
compile_flags.txt
# Auto-generated resource script
resources/environment.sh
# Auto-generated resource script for xcode
resources/build/mac/xcode-environment.inc.sh
# Copy of the master VERSION.md used for Linux packaging
/core/VERSION.md
@ -164,7 +164,7 @@ tsconfig.tsbuildinfo
# Windows system files
Thumbs.db
# Temporary file for logging scripts in xcode runs, see build-utils.sh for
# Temporary file for logging scripts in xcode runs, see builder-basic.inc.sh for
# details
/xcodebuild-scripts.log

View file

@ -1,5 +1,6 @@
external-sources=true
# If you get SC1091 for sourced files, add the following setting to
# your .vscode/settings.json file:
# "shellcheck.useWorkspaceRootAsCwd": false
source-path=resources/build/
source-path=resources/
source-path=SCRIPTDIR
source-path=linux/ibus-keyman/tests/scripts/

View file

@ -1,5 +1,258 @@
# Keyman Version History
## 19.0.128 alpha 2025-09-26
* fix(windows): sort setup UI language selector according to language names (#13573)
* feat(windows): improve startup time by caching locale names (#14795)
## 19.0.127 alpha 2025-09-25
* fix(common): registry clean-up enabled, no terminal in github action (#14824)
* maint(windows): check code signatures only in CI builds (#14815)
* chore(deps-dev): bump tar-fs from 3.0.9 to 3.1.1 (#14827)
* fix(windows): disable MSI shortcut advertisement (#14814)
* maint(resources): lookup 7z on path on Windows if SEVENZ_HOME is not set (#14816)
* fix(android): Apply bottom insets to system keyboard (#14808)
* fix(android): Fix bottom insets for Sample and FirstVoices apps (#14828)
* docs(android): Document getNavigationBarHeight API (#14830)
## 19.0.126 alpha 2025-09-24
* maint(linux): remove several shellcheck directives (#14819)
## 19.0.125 alpha 2025-09-23
* fix(common): some more tests running successfully in a container (#14784)
## 19.0.124 alpha 2025-09-22
* maint(linux): improve `upload-to-debian` script (#14798)
* refactor(linux): remove a meson deprecation warning (#14801)
* refactor(linux): remove shellcheck warnings (#14802)
* refactor(resources): use array for Docker arguments (#14803)
## 19.0.123 alpha 2025-09-19
* chore(linux): Update debian changelog (#14783)
## 19.0.122 alpha 2025-09-18
* fix(android): Refresh the banner theme when system keyboard is changed (#14768)
* maint(common): update HISTORY.md for 18.0.241 (#14780)
## 19.0.121 alpha 2025-09-17
* docs(developer): document `fileVersion` (#14766)
* maint(linux): allow to force package build on non-Keyman repo (#14749)
* feat(linux): add breadcrumbs and system information to Sentry error reports (#14765)
* maint(resources): fix broken test build trigger for `master` branch (#14770)
* chore(linux): remove automatic installation of `onboard-keyman` (#14772)
## 19.0.120 alpha 2025-09-16
* refactor(linux): remove warning (#14761)
* fix(linux): don't crash on invalid languages in keyboard (#14760)
## 19.0.119 alpha 2025-09-15
* fix(developer): transform slashes to backslashes in paths in Package Editor (#14721)
* fix(developer): restrict invalid characters in identifiers in kmcmplib (#14746)
* fix(common): add name for cleanup ghcr (#14755)
## 19.0.118 alpha 2025-09-12
* chore(common): Update to Unicode 17.0 (#14728)
* fix(resources): match cr to lf change in pr-build-status.mjs (#14734)
* fix(common): merging build and publish step for docker (#14747)
* chore(developer): use icons.res to verify that configure is done (#14733)
## 19.0.117 alpha 2025-09-11
* fix(resources): ignore KEYMAN_VERSION_FOR_FILENAME in test (#14730)
* fix(resources): pr build status - find 'failure' state in status checks and log (#14732)
## 19.0.116 alpha 2025-09-10
* fix(windows): kmshell continues when sm is in waitingrestart and returns to idle (#14644)
* maint(linux): allow to skip API check (#14699)
* maint(resources): adding GitHub actions workflow for Docker builds (#14674)
* maint(developer): use versioned filenames for PR artifacts (#14701)
* maint(windows): use versioned filenames for PR artifacts (#14702)
* maint(android): use versioned filenames for PR artifacts (#14703)
* maint(ios): use versioned filenames for PR artifacts (#14704)
* maint(mac): use versioned filenames for PR artifacts (#14705)
* maint(resources): rename KEYMAN_VERSION_FOR_PR_FILENAME (#14720)
## 19.0.115 alpha 2025-09-09
* fix(linux): don't crash if `kmp.json` is missing `keyboards` section (#14708)
## 19.0.114 alpha 2025-09-08
* docs(web): Improve empty What's New page for Web (#14660)
* maint(linux): remove obsolete Makefile (#14694)
* maint(resources): honor configure comment character in commit message (#14695)
* fix(web): fix error trying to calculate style scaling effects (#14684)
* refactor(resources): remove shellcheck warnings (#14698)
## 19.0.113 alpha 2025-09-04
* fix(android): Update KMSample1 layout when changing orientation (#14678)
## 19.0.112 alpha 2025-09-03
* fix(developer): add clear messages about validation failures to New LM Project Parameters form (#14659)
* docs(developer): tweak get-started for kmc and add entry to HISTORY.md (#14667)
* fix(android): Cleanup list layout in Engine and FirstVoices for edge to edge (#14655)
* docs(common): update license date and owner (#14668)
* maint(linux): skip tarball creation for `Build-bot: build` buildLevel (#14671)
* docs(common): update docs Keyman Desktop → Keyman for Windows (#14669)
* feat(web): adds transform cataloging methods to predictive-text worker (#14581)
* feat(web): batch correction-search paths by related subsets (#14592)
* refactor(web): simplify edit-distance calculation-object inputs (#14618)
## 19.0.111 alpha 2025-09-02
* change(developer): update SIL logo (#14656)
* fix(developer): remove incorrect offset for compiler error line (#14651)
* fix(developer): add clear messages about validation failures to New Project Parameters form (#14657)
* fix(developer): add clear messages about validation failures to New LDML Project Parameters form (#14658)
* maint: add registry usage for Docker images (#14648)
## 19.0.110 alpha 2025-09-01
* maint(windows): for `build` buildlevel, do not publish artifacts (#14654)
* maint(linux): generate tar-ignore in `dist.sh` (#14639)
* fix: fixing some issues for bulding and testing with Docker containers (#14645)
* maint(linux): create temporary worktree for packaging (#14598)
## 19.0.109 alpha 2025-08-31
* fix(developer): handle missing model id in package with compiler error (#14649)
* fix(developer): handle failure to create destination path when generating new project (#14650)
## 19.0.108 alpha 2025-08-29
* fix(android): Fix insets for in-app vs system keyboard (#14619)
* maint(resources): ensure REPO_ROOT is fully resolved (#14643)
## 19.0.107 alpha 2025-08-28
* maint(resources): fix parse bug in build-bot and add extra logging (#14611)
* fix(android): Update Gradle files to remove flatDir warnings (#14583)
* maint(resources): move build trigger scripts into resources/teamcity (#14607)
* chore(linux): Update debian changelog (#14590)
* docs: update package-editor page section details tab (#14585)
* fix(android): polyfill globalThis for API 28 devices (#14541)
* feat: support pagination in build bot PR commit reading (#14638)
* refactor(linux): remove shellcheck warnings and reformat (#14572)
## 19.0.106 alpha 2025-08-27
* fix(windows): check if automatic updates off before prompt (#14295)
* refactor(linux): use bash testing framework for packaging tests (#14603)
* maint(resources): use correct version for PR title for history cherry-pick (#14610)
* refactor(common): fix shellcheck warnings (#14580)
## 19.0.105 alpha 2025-08-25
* maint(linux): ignore generate patch directories (#14600)
## 19.0.104 alpha 2025-08-21
* fix(android): Update sentry-android dependency to 8.19.1 for 16 KB page alignment (#14559)
* maint(android): Cleanup builder_describe_outputs for configure action (#14566)
* fix(windows): emit nunit report path correctly and update TeamCity links (#14564)
* maint(common): rename and move node-related script functions into node.inc.sh (#14568)
## 19.0.103 alpha 2025-08-20
* fix(android): Show system OSK when physical keyboard connected on Android API 36 (#14538)
* fix(android): Add "Show On-screen keyboard" preference to display OSK when physical keyboard is connected (#14539)
* fix(android): Add FirstVoices preference to show on-screen keyboard (#14546)
* fix(linux): normalize language tags from keyboard (#14542)
* maint(common): fix running docker on Windows (#14154)
* maint(mac): use mac_ prefix for functions in mac.inc.sh (#14562)
* maint(android): publish symbols to Sentry on release buildLevel (#14490)
* maint(linux): add missing `--help.keyman.com` option (#14571)
* docs(web): improve documentation for SearchSpace and SearchNode (#14555)
## 19.0.102 alpha 2025-08-19
* maint(mac): skip unnecessary network steps when buildLevel is build (#14467)
* maint(common): cleanup and reorganize ci scripts (#14459)
* refactor(linux): cleanup some code in bash completion script (#14543)
* maint(resources): handle build-bot skip correctly (#14551)
* maint(resources): don't fall back to 'build' action (#14552)
* maint(resources): handle unset build platform in trigger-build-bot (#14554)
## 19.0.101 alpha 2025-08-18
* maint(android): Update Android Target API to 35 (#14395)
* fix(android): Convert Keyman app layout to use ConstraintLayout for handling edge-to-edge (#14396)
* fix(android): Update other activities for edge to edge (#14463)
* fix(android): Update Sample, Test, and FirstVoices apps for edge to edge (#14514)
* maint(common): rename and move node-related script functions into node.inc.sh (#14540)
## 19.0.100 alpha 2025-08-14
* maint(resources): add space to test-bot command (#14528)
* fix(web): improve accuracy of types for suggestions & suggestion-generation (#14517)
* refactor(web): move applySuggestionCasing to standalone exportable method (#14518)
* chore(web): convert worker-thread tests to TS (#14510)
## 19.0.99 alpha 2025-08-14
* maint(web): skip unnecessary steps for `build` buildLevel (#14516)
## 19.0.98 alpha 2025-08-14
* maint(resources): specify `keymanapp/keyman` for gh in automerge (#14524)
## 19.0.97 alpha 2025-08-14
* maint(resources): use GH_TOKEN variable for gh calls (#14522)
## 19.0.96 alpha 2025-08-14
* maint(resources): add build-bot commands to increment-version auto PRs (#14504)
* maint(resources): use GH builtin automerge for auto PRs (#14519)
## 19.0.95 alpha 2025-08-13
* fix(web): use actual legacy model for legacy-model support unit tests (#14460)
* chore(common): Update Crowdin strings for Spanish (#14464)
* fix(android): Get default sizes before initializing keyboard (#14472)
* docs(resources): clarify Build-bot usage (#14282)
* maint(resources): build.sh-style commands for build-bot (#14461)
* chore(android): Update keycode test app dependencies (#14474)
* maint(common): update npm modules brace-expansion to 1.1.12, form-data to 4.0.4 (#14479)
* chore(android): Specify Java language version 21 (#14491)
* maint(ios): skip unnecessary network steps when buildLevel is build (#14466)
* maint(common): rename shellHelperFunctions.sh to utils.inc.sh (#14446)
* maint(common): consolidate builder scripts (#14449)
* maint(common): rename and move node-related script functions into node.inc.sh (#14451)
* maint(common): move typescript script functions into typescript.inc.sh (#14452)
* maint(common): reorganize mac/ios build script includes (#14458)
* docs(resources): add note on builder-basic vs builder-full (#14507)
## 19.0.94 alpha 2025-08-01
* maint(common): use `cpp_std=c++17` in meson project default_options (#14433)
## 19.0.93 alpha 2025-07-29
* test(developer): add unit tests for `isValidUtf8` (#14381)
* maint(linux): fix stopping of test server (#14403)
## 19.0.92 alpha 2025-07-28
* build(deps): bump multer from 2.0.1 to 2.0.2 (#14373)
* refactor(linux): remove shellcheck warnings (#14409)
* refactor(linux): move dbus defines to common include (#14407)
* docs(linux): update readme for test service (#14408)
* fix(windows): add check a fix for registry datatypes (#14358)
## 19.0.91 alpha 2025-07-26
* maint(windows): fix calls of renamed functions (#14413)
@ -557,6 +810,43 @@
* refactor(windows): rename `TKeymanMutex.MutexOwned` to `TakeOwnership` and add `ReleaseOwnership` (#13168)
* chore: increment to alpha 19.0 (#13187)
## 18.0.241 stable 2025-09-18
* maint(resources): use correct version for PR title for history cherry-pick (#14614)
* chore(linux): Update debian changelog (#14589)
* fix(android): Update: Android Target API to 35, Java to 21, and Gradle dependencies (#14584)
* fix(android): Convert Keyman app layouts for edge to edge (#14597)
* fix(android): Fix insets for in-app vs system keyboard (#14642)
* maint(linux): create temporary worktree for packaging (#14599)
* fix(android): Cleanup layouts for Engine, Sample apps, and FirstVoices apps for edge to edge (#14663)
* fix(web): fix error trying to calculate style scaling effects (#14696)
* fix(linux): don't crash if `kmp.json` is missing `keyboards` section (#14714)
* fix(developer): transform slashes to backslashes in paths in Package Editor (#14722)
* chore(linux): remove automatic installation of `onboard-keyman` (#14773)
* docs(linux): update release notes for stable release (#14774)
* fix(linux): don't crash on invalid languages in keyboard (#14771)
## 18.0.240 stable 2025-08-27
* maint(resources): automatically cherry-pick history updates to master (#14596)
* maint(linux): fix lintian warnings (#14591)
* maint(linux): ignore generate patch directories (#14601)
## 18.0.239 stable 2025-08-22
* docs(linux): clarify requirement of patched ibus for Keyman < 18 (#14113)
* chore(linux): Update debian changelog (#14118)
* fix(windows): add values wucUpdateAvailable and wucNotChecked to TRemoteUpdateCheckResult enum (#14208)
* fix(windows): fix download failing when cache.json missing (#14207)
* fix(web): deploy `web/build/publish` folder (#14238)
* fix(ios): restore keyboard scale map for iPad Air 2 (#14306)
* chore(developer): upgrade multer to 2.0.2 (#14417)
* fix(android): cherry-pick getting default sizes before initializing keyboard (#14473)
* fix(android): polyfill globalThis for API 28 devices (#14553)
* fix(android): Update sentry-android dependency to 7.22.6 for 16 KB page alignment (#14560)
* fix(android): Show system OSK when physical keyboard connected (#14547)
* fix(android): Add preference to show on-screen keyboard to Keyman and FirstVoices apps (#14561)
## 18.0.238 stable 2025-06-04
* fix(developer): ldml: make sure .run() calls compile() for validation (#14071)

View file

@ -1 +1 @@
19.0.92
19.0.129

View file

@ -3,19 +3,12 @@ buildscript {
repositories {
google()
mavenCentral()
flatDir {
dirs "kMAPro/libs/com/stepstone/stepper/material-stepper/4.3.1/"
}
}
dependencies {
classpath 'com.android.tools.build:gradle:8.9.2'
// sentry-android-gradle-plugin 2.1.5+ requires AGP 7.0
classpath 'io.sentry:sentry-android-gradle-plugin:4.6.0'
classpath 'io.sentry:sentry-android-gradle-plugin:5.9.0'
classpath 'name.remal:gradle-plugins:1.9.2'
// From jcenter() which was deprecated August 2024
// https://jfrog.com/blog/into-the-sunset-bintray-jcenter-gocenter-and-chartcenter/
classpath 'com.stepstone.stepper:material-stepper:4.3.1@aar'
}
}

View file

@ -3,11 +3,10 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../resources/build/builder.inc.sh"
. "${THIS_SCRIPT%/*}/../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
. "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh"
. "$KEYMAN_ROOT/resources/build/utils.inc.sh"
. "$KEYMAN_ROOT/resources/build/build-help.inc.sh"
. "$KEYMAN_ROOT/resources/build/build-download-resources.sh"
@ -28,8 +27,8 @@ builder_describe "Builds Keyman for Android app." \
"configure" \
"build" \
"test Runs lint and unit tests." \
"publish Publishes symbols to Sentry and the APK to the Play Store." \
"--upload-sentry Upload to sentry"
"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 "$@"
@ -41,9 +40,13 @@ if builder_is_debug_build; then
TEST_FLAGS="-x assembleDebug lintDebug testDebug"
fi
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 /android/KMAPro/kMAPro/libs/keyman-engine.aar \
build /android/KMAPro/kMAPro/build/outputs/apk/$CONFIG/keyman-${KEYMAN_VERSION}.apk
configure "/${MODELS_TARGET}" \
build /android/KMAPro/kMAPro/build/outputs/apk/$CONFIG/keyman-${KEYMAN_VERSION_FOR_FILENAME}.apk
#### Build
@ -54,18 +57,6 @@ if builder_is_ci_build; then
DAEMON_FLAG=--no-daemon
fi
#### Build action definitions ####
function makeLocalSentryRelease() {
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 ./
echo "Finalizing release tag $KEYMAN_VERSION_GIT_TAG"
sentry-cli releases finalize "$KEYMAN_VERSION_GIT_TAG"
}
#### Build action definitions ####
# Check about cleaning artifact paths
@ -76,13 +67,8 @@ fi
if builder_start_action configure; then
KEYBOARD_PACKAGE_ID="sil_euro_latin"
KEYBOARDS_TARGET="$KEYMAN_ROOT/android/KMAPro/kMAPro/src/main/assets/${KEYBOARD_PACKAGE_ID}.kmp"
MODEL_PACKAGE_ID="nrc.en.mtnt"
MODELS_TARGET="$KEYMAN_ROOT/android/KMAPro/kMAPro/src/main/assets/${MODEL_PACKAGE_ID}.model.kmp"
downloadKeyboardPackage "$KEYBOARD_PACKAGE_ID" "$KEYBOARDS_TARGET"
downloadModelPackage "$MODEL_PACKAGE_ID" "$MODELS_TARGET"
downloadKeyboardPackage "$KEYBOARD_PACKAGE_ID" "${KEYMAN_ROOT}/$KEYBOARDS_TARGET"
downloadModelPackage "$MODEL_PACKAGE_ID" "${KEYMAN_ROOT}/$MODELS_TARGET"
builder_finish_action success configure
fi
@ -90,7 +76,7 @@ fi
if builder_start_action build; then
# 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-${CONFIG}.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
@ -98,9 +84,7 @@ if builder_start_action build; then
echo "BUILD_FLAGS $BUILD_FLAGS"
./gradlew $DAEMON_FLAG clean $BUILD_FLAGS
if builder_has_option --upload-sentry; then
makeLocalSentryRelease
fi
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"
builder_finish_action success build
fi
@ -113,12 +97,23 @@ if builder_start_action test; then
builder_finish_action success test
fi
if builder_start_action publish; then
# Copy Release Notes
generateReleaseNotes
publish_symbols() {
if builder_is_ci_build && builder_is_ci_build_level_release; then
# TODO: what does publishSentry even do?
./gradlew $DAEMON_FLAG 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 symbols and Keyman for Android to Play Store
./gradlew $DAEMON_FLAG publishSentry publishReleaseApk
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
fi
}
builder_finish_action success publish
fi
builder_run_action publish-symbols publish_symbols
builder_run_action publish-play-store publish_play_store

View file

@ -11,8 +11,14 @@ apply from: "$rootPath/version.gradle"
String tier = new File('../../TIER.md').getText('UTF-8').trim();
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
android {
compileSdk 34
compileSdk 35
namespace="com.tavultesoft.kmapro"
// Don't compress kmp files so they can be copied via AssetManager
@ -24,15 +30,10 @@ android {
buildConfig = true
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
defaultConfig {
applicationId "com.tavultesoft.kmapro"
minSdkVersion 21
targetSdkVersion 34
targetSdkVersion 35
//println "===DUMPING PROPERTIES==="
//dumpProperties(project) // Use this to dump all external properties for debugging TeamCity integration
@ -113,6 +114,12 @@ android {
disable 'MissingQuantity', 'MissingTranslation'
lintConfig file("lint.xml")
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
// how to configure the sentry android gradle plugin
@ -157,19 +164,17 @@ if (env_keys_json_file != null && file(env_keys_json_file).exists()) {
}
repositories {
flatDir {
dirs 'libs'
}
google()
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.7.0'
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'
api(name: 'keyman-engine', ext: 'aar')
implementation 'io.sentry:sentry-android:7.8.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"

View file

@ -5,10 +5,10 @@
package com.keyman.android;
import com.keyman.engine.util.DownloadFileUtils;
import com.tavultesoft.kmapro.AdjustLongpressDelayActivity;
import com.tavultesoft.kmapro.BuildConfig;
import com.tavultesoft.kmapro.DefaultLanguageResource;
import com.tavultesoft.kmapro.KeymanSettingsActivity;
import com.tavultesoft.kmapro.PreferencesManager;
import com.keyman.engine.KMManager;
import com.keyman.engine.KMManager.KeyboardType;
import com.keyman.engine.KMHardwareKeyboardInterpreter;
@ -30,7 +30,6 @@ import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
@ -125,8 +124,9 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
}
ViewGroup parent = (ViewGroup) inputView.getParent();
if (parent != null)
if (parent != null) {
parent.removeView(inputView);
}
return inputView;
}
@ -246,9 +246,10 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
inputViewHeight = inputView.getHeight();
}
int navigationHeight = KMManager.getNavigationBarHeight(this);
int bannerHeight = KMManager.getBannerHeight(this);
int kbHeight = KMManager.getKeyboardHeight(this);
outInsets.contentTopInsets = inputViewHeight - bannerHeight - kbHeight;
outInsets.contentTopInsets = inputViewHeight - bannerHeight - kbHeight - navigationHeight;
outInsets.visibleTopInsets = outInsets.contentTopInsets;
outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
outInsets.touchableRegion.set(0, outInsets.contentTopInsets, size.x, size.y);
@ -264,6 +265,8 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
@Override
public void onKeyboardChanged(String newKeyboard) {
// Refresh banner theme
BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_SYSTEM);
KMManager.showSystemKeyboard();
}
@ -278,6 +281,21 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
// Do nothing
}
@Override
public boolean onEvaluateInputViewShown() {
// On Android API 36+, the OSK defaults to not appearing when a physical keyboard is connected.
// If the default implementation returns true, recommend honoring it
// Reference: https://android.googlesource.com/platform/frameworks/base/+/7b739a8%5E%21/
if (super.onEvaluateInputViewShown()) {
return true;
};
Context context = getApplicationContext();
SharedPreferences prefs = context.getSharedPreferences(PreferencesManager.kma_prefs_name, Context.MODE_PRIVATE);
boolean showOSK = prefs.getBoolean(KeymanSettingsActivity.oskWithPhysicalKeyboardKey, false);
return showOSK;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// Determine if Physical keystroke should be passed off

View file

@ -44,6 +44,9 @@ public class AdjustKeyboardHeightActivity extends BaseActivity {
setContentView(R.layout.activity_adjust_keyboard_height);
setupEdgeToEdge(R.id.adjust_keyboard_layout);
setupStatusBarColors(R.color.keyman_blue, android.R.color.white);
Toolbar toolbar = (Toolbar) findViewById(R.id.titlebar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();

View file

@ -34,6 +34,7 @@ public class InfoActivity extends BaseActivity {
final Context context = this;
setContentView(R.layout.activity_info);
setupEdgeToEdge(R.id.info_layout);
// Navigation buttons
final ImageButton backButton = (ImageButton) findViewById(R.id.back_button);

View file

@ -79,6 +79,9 @@ public class KMPBrowserActivity extends BaseActivity {
return;
}
setupEdgeToEdge(R.id.kmp_browser_layout);
setupStatusBarColors(R.color.complementary_5, android.R.color.white);
webView = (WebView) findViewById(R.id.kmpBrowserWebView);
webView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
webView.getSettings().setJavaScriptEnabled(true);

View file

@ -5,7 +5,6 @@ import android.os.Bundle;
import com.keyman.engine.BaseActivity;
import com.keyman.engine.KMManager;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
public class KeymanSettingsActivity extends BaseActivity {
@ -14,6 +13,7 @@ public class KeymanSettingsActivity extends BaseActivity {
protected static final String sendCrashReport = "SendCrashReport";
public static final String spacebarTextKey = "SpacebarText";
public static final String hapticFeedbackKey = "HapticFeedback";
public static final String oskWithPhysicalKeyboardKey = "ShowOSK";
protected KeymanSettingsFragment innerFragment;
@ -22,6 +22,10 @@ public class KeymanSettingsActivity extends BaseActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.keyman_settings_layout);
setupEdgeToEdge(R.id.keyman_settings_layout);
setupStatusBarColors(R.color.keyman_blue, android.R.color.white);
Toolbar toolbar = (Toolbar) findViewById(R.id.keyman_settings_toolbar);
toolbar.setTitleTextColor(getResources().getColor(android.R.color.white));
toolbar.setTitle(R.string.keyman_settings);

View file

@ -6,6 +6,7 @@ package com.tavultesoft.kmapro;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.provider.Settings;
import android.view.inputmethod.InputMethodManager;
@ -20,6 +21,7 @@ import androidx.preference.SwitchPreference;
import com.keyman.engine.DisplayLanguages;
import com.keyman.engine.KMManager;
import com.keyman.engine.data.Dataset;
import com.tavultesoft.kmapro.PreferencesManager;
import java.util.HashMap;
@ -157,6 +159,31 @@ public class KeymanSettingsFragment extends PreferenceFragmentCompat {
}
});
SwitchPreference oskWithPhysicalKeyboardPreference = new SwitchPreference(context);
oskWithPhysicalKeyboardPreference.setKey(KeymanSettingsActivity.oskWithPhysicalKeyboardKey);
oskWithPhysicalKeyboardPreference.setTitle(getString(R.string.show_osk));
oskWithPhysicalKeyboardPreference.setSummary(getString(R.string.show_osk_hint));
SharedPreferences prefs = context.getSharedPreferences(
PreferencesManager.kma_prefs_name, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
boolean showOSK = prefs.getBoolean(
KeymanSettingsActivity.oskWithPhysicalKeyboardKey, false);
oskWithPhysicalKeyboardPreference.setDefaultValue(showOSK);
oskWithPhysicalKeyboardPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (newValue == null) {
return false;
}
editor.putBoolean(KeymanSettingsActivity.oskWithPhysicalKeyboardKey, (boolean)newValue);
editor.commit();
return true;
}
});
SwitchPreference hapticFeedbackPreference = new SwitchPreference(context);
hapticFeedbackPreference.setKey(KeymanSettingsActivity.hapticFeedbackKey);
hapticFeedbackPreference.setTitle(getString(R.string.haptic_feedback));
@ -198,6 +225,8 @@ public class KeymanSettingsFragment extends PreferenceFragmentCompat {
screen.addPreference(adjustKeyboardHeight);
screen.addPreference(adjustLongpressDelay);
screen.addPreference(oskWithPhysicalKeyboardPreference);
screen.addPreference(spacebarTextPreference);
screen.addPreference(hapticFeedbackPreference);

View file

@ -7,7 +7,6 @@ import java.util.ArrayList;
import java.util.HashMap;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.content.Context;
@ -24,11 +23,12 @@ import android.os.Bundle;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import com.keyman.engine.BaseActivity;
import com.keyman.engine.KMManager;
import com.keyman.engine.data.KeyboardController;
import com.keyman.engine.util.MapCompat;
public class KeymanSettingsInstallActivity extends AppCompatActivity {
public class KeymanSettingsInstallActivity extends BaseActivity {
private static final String TAG = "SettingsInstallActivity";
private static ArrayList<HashMap<String, String>> installOptionList = null;
private static Typeface titleFont = null;
@ -45,6 +45,8 @@ public class KeymanSettingsInstallActivity extends AppCompatActivity {
context = this;
setContentView(R.layout.activity_list_layout);
setupEdgeToEdge(R.id.list_toolbar);
final Toolbar toolbar = findViewById(R.id.list_toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);

View file

@ -25,10 +25,10 @@ import android.widget.TextView;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SwitchCompat;
import androidx.appcompat.widget.Toolbar;
import com.keyman.engine.BaseActivity;
import com.keyman.engine.KeyboardPickerActivity;
import com.keyman.engine.KMManager;
import com.keyman.engine.ModelPickerActivity;
@ -43,7 +43,7 @@ import java.util.HashMap;
* Keyman Settings --> Languages Settings --> Language Settings
* Displays a list of installed keyboards and some lexical model switches.
*/
public final class LanguageSettingsActivity extends AppCompatActivity {
public final class LanguageSettingsActivity extends BaseActivity {
private Context context;
private static Toolbar toolbar = null;
private static ListView listView = null;
@ -66,6 +66,8 @@ public final class LanguageSettingsActivity extends AppCompatActivity {
context = this;
setContentView(R.layout.language_settings_list_layout);
setupEdgeToEdge(R.id.list_toolbar);
toolbar = (Toolbar) findViewById(R.id.list_toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);

View file

@ -19,9 +19,9 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import com.keyman.engine.BaseActivity;
import com.keyman.engine.data.Dataset;
import com.keyman.engine.data.adapters.AdapterFilter;
import com.keyman.engine.data.adapters.NestedAdapter;
@ -36,7 +36,7 @@ import java.util.List;
* Keyman Settings --> Languages Settings
* Displays a list of installed languages and a count of their associated installed keyboards.
*/
public final class LanguagesSettingsActivity extends AppCompatActivity {
public final class LanguagesSettingsActivity extends BaseActivity {
private Context context;
private static Toolbar toolbar = null;
@ -53,6 +53,8 @@ public final class LanguagesSettingsActivity extends AppCompatActivity {
context = this;
setContentView(R.layout.languages_settings_list_layout);
setupEdgeToEdge(R.id.languages_list_layout);
toolbar = (Toolbar) findViewById(R.id.list_toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);

View file

@ -67,6 +67,7 @@ import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.widget.Toolbar;
import androidx.appcompat.app.AlertDialog;
import androidx.constraintlayout.widget.ConstraintLayout;
import android.content.ClipData;
import android.content.Context;
import android.content.DialogInterface;
@ -85,7 +86,6 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.provider.Settings;
import android.text.Html;
@ -98,8 +98,6 @@ import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.SeekBar;
@ -118,6 +116,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
private static final String TAG = "MainActivity";
private ConstraintLayout constraintLayout;
private KMTextView textView;
private final int minTextSize = 16;
private final int maxTextSize = 72;
@ -170,6 +169,10 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
setContentView(R.layout.activity_main);
constraintLayout = (ConstraintLayout)findViewById(R.id.constraintLayout);
setupEdgeToEdge(R.id.constraintLayout);
setupStatusBarColors(android.R.color.white, R.color.neutral_2);
toolbar = (Toolbar) findViewById(R.id.titlebar);
setSupportActionBar(toolbar);
getSupportActionBar().setTitle(null);
@ -622,10 +625,18 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
if (resourceId > 0)
statusBarHeight = getResources().getDimensionPixelSize(resourceId);
// Navigation bar height
int navigationBarHeight = 0;
resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
navigationBarHeight = getResources().getDimensionPixelSize(resourceId);
}
Point size = KMManager.getWindowSize(context);
int screenHeight = size.y;
Log.d(TAG, "Main resizeTextView bannerHeight: " + bannerHeight);
textView.setHeight(screenHeight - statusBarHeight - actionBarHeight - bannerHeight - keyboardHeight);
textView.setHeight(
screenHeight - statusBarHeight - actionBarHeight - bannerHeight - keyboardHeight - navigationBarHeight);
}
private void showInfo() {

View file

@ -4,7 +4,6 @@
package com.tavultesoft.kmapro;
import android.annotation.SuppressLint;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
@ -18,6 +17,7 @@ import android.widget.Toast;
import com.stepstone.stepper.StepperLayout;
import com.stepstone.stepper.VerificationError;
import com.keyman.engine.BaseActivity;
import com.keyman.engine.KMManager;
import com.keyman.engine.KeyboardEventHandler;
import com.keyman.engine.KmpInstallMode;
@ -34,7 +34,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class PackageActivity extends AppCompatActivity implements
public class PackageActivity extends BaseActivity implements
StepperLayout.StepperListener, WebViewFragment.OnInstallClickedListener,
SelectLanguageFragment.OnLanguagesSelectedListener {
private final static String TAG = "PackageActivity";
@ -52,6 +52,7 @@ public class PackageActivity extends AppCompatActivity implements
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_package_installer);
setupEdgeToEdge(R.id.package_installer_layout);
KmpInstallMode installMode = KmpInstallMode.Full;
String languageID = null;

View file

@ -0,0 +1,11 @@
/**
* Keyman is copyright (C) SIL Global. MIT License.
*
*/
package com.tavultesoft.kmapro;
public class PreferencesManager {
// TODO: Separate this from the Keyman Engine preference key name in strings.xml: #13633
public static final String kma_prefs_name = "KMAPreferences";
}

View file

@ -8,11 +8,11 @@ import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import com.stepstone.stepper.StepperLayout;
import com.stepstone.stepper.VerificationError;
import com.keyman.engine.BaseActivity;
import com.keyman.engine.KMManager;
import com.keyman.engine.data.Keyboard;
import com.keyman.engine.packages.PackageProcessor;
@ -29,7 +29,7 @@ import java.util.List;
* Also available during keyboard package installation that involves a language choice.
* Displays a list of available language names for the user to add for a given installed packageID/keyboardID.
*/
public class SelectLanguageActivity extends AppCompatActivity implements
public class SelectLanguageActivity extends BaseActivity implements
StepperLayout.StepperListener, SelectLanguageFragment.OnLanguagesSelectedListener {
private final static String TAG = "SelectLanguageActivity";
private File packagePath;
@ -43,6 +43,7 @@ public class SelectLanguageActivity extends AppCompatActivity implements
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_select_language);
setupEdgeToEdge(R.id.select_language_layout);
Bundle bundle = getIntent().getExtras();
keyboard = (Keyboard)bundle.getSerializable("keyboard");

View file

@ -16,9 +16,9 @@ import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import com.keyman.engine.BaseActivity;
import com.keyman.engine.KMManager;
import com.keyman.engine.data.Keyboard;
import com.keyman.engine.data.KeyboardController;
@ -33,7 +33,7 @@ import java.util.List;
* Keyman Settings --> KeymanInstallActivity --> SelectPackageActivity
* Displays a list of installed package ID / keyboard IDs so the user can select a language from the kmp.json
*/
public final class SelectPackageActivity extends AppCompatActivity {
public final class SelectPackageActivity extends BaseActivity {
private static final String TAG = "SelectPackageActivity";
private static ArrayList<HashMap<String, String>> list = null;
private static Typeface titleFont = null;
@ -49,6 +49,8 @@ public final class SelectPackageActivity extends AppCompatActivity {
context = this;
setContentView(R.layout.activity_list_layout);
setupEdgeToEdge(R.id.activity_list_layout);
final Toolbar toolbar = findViewById(R.id.list_toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);

View file

@ -1,9 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:id="@+id/adjust_keyboard_layout"
android:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/titlebar" />
@ -15,7 +17,7 @@
android:layout_marginStart="@dimen/activity_horizontal_margin"
android:text="@string/drag_the_keyboard"
android:textSize="@dimen/adjust_keyboard_height_label_textsize"
/>
app:layout_constraintTop_toBottomOf="@id/titlebar" />
<TextView
android:id="@+id/rotate_the_device"
@ -25,23 +27,35 @@
android:layout_marginStart="@dimen/activity_horizontal_margin"
android:text="@string/rotate_the_device"
android:textSize="@dimen/adjust_keyboard_height_label_textsize"
/>
app:layout_constraintTop_toBottomOf="@id/drag_the_keyboard" />
<Button
android:id="@+id/reset_to_defaults"
style="@style/Widget.AppCompat.Button.Colored"
android:background="@color/keyman_blue"
android:textColor="@color/ms_white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/rotate_the_device"
android:layout_centerInParent="true"
android:singleLine="true"
android:layout_marginTop="8dp"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:text="@string/reset_to_defaults"
android:textSize="@dimen/adjust_keyboard_height_label_textsize" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/button_layout"
android:orientation="horizontal"
android:layout_below="@id/rotate_the_device"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/rotate_the_device">
<Button
android:id="@+id/reset_to_defaults"
style="@style/Widget.AppCompat.Button.Colored"
android:background="@color/keyman_blue"
android:textColor="@color/ms_white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:singleLine="true"
android:layout_marginTop="8dp"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:text="@string/reset_to_defaults"
android:textSize="@dimen/adjust_keyboard_height_label_textsize"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- Resizable keyboard "image -->
<ImageView
@ -56,6 +70,10 @@
android:paddingStart="@dimen/blank_osk_padding"
android:paddingEnd="@dimen/blank_osk_padding"
android:paddingTop="@dimen/blank_osk_padding"
android:paddingBottom="@dimen/blank_osk_padding"/>
android:paddingBottom="@dimen/blank_osk_padding"
app:layout_constraintTop_toBottomOf="@id/button_layout"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintVertical_bias="1.0" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,75 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/info_layout"
android:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/ms_white"
tools:context=".InfoActivity" >
<androidx.appcompat.widget.Toolbar
android:id="@+id/titlebar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_alignParentTop="true"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
<androidx.appcompat.widget.Toolbar
android:id="@+id/titlebar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/close_button" >
<!-- Navigation buttons in a LinearLayout to mirror on RTL -->
<ImageButton
android:id="@+id/back_button"
style="?android:attr/actionButtonStyle"
app:tint="@color/image_button_color"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:contentDescription="@string/label_back"
android:src="@drawable/ic_action_back" />
<!-- Navigation buttons in a LinearLayout to mirror on RTL -->
<ImageButton
android:id="@+id/back_button"
style="?android:attr/actionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:contentDescription="@string/label_back"
android:src="@drawable/ic_action_back"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tint="@color/image_button_color" />
<ImageButton
android:id="@+id/forward_button"
style="?android:attr/actionButtonStyle"
app:tint="@color/image_button_color"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/label_forward"
android:src="@drawable/ic_action_forward" />
</LinearLayout>
<ImageButton
android:id="@+id/forward_button"
style="?android:attr/actionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:contentDescription="@string/label_forward"
android:src="@drawable/ic_action_forward"
app:layout_constraintStart_toEndOf="@id/back_button"
app:layout_constraintTop_toTopOf="parent"
app:tint="@color/image_button_color" />
<ImageButton
android:id="@+id/close_button"
style="?android:attr/actionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:contentDescription="@string/label_close"
android:src="@drawable/ic_action_close" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.appcompat.widget.Toolbar>
<ImageButton
android:id="@+id/close_button"
style="?android:attr/actionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="end"
android:contentDescription="@string/label_close"
android:src="@drawable/ic_action_close"
app:layout_constraintEnd_toEndOf="parent" />
<WebView
android:id="@+id/infoWebView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/titlebar"
android:layout_marginBottom="@dimen/info_label_height" />
</androidx.appcompat.widget.Toolbar>
<!-- layout_height 0dp to fill the remaining area -->
<WebView
android:id="@+id/infoWebView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_below="@id/titlebar"
android:layout_marginStart="4dp"
app:layout_constraintTop_toBottomOf="@id/titlebar"
app:layout_constraintBottom_toTopOf="@+id/infoVersion"
app:layout_constraintStart_toStartOf="parent" />
<!-- Version string on the bottom of the page -->
<TextView
android:id="@+id/infoVersion"
android:layout_width="match_parent"
android:layout_height="@dimen/info_label_height"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:ellipsize="end"
android:singleLine="true"
android:text="@string/title_version"
android:textSize="@dimen/info_label_textsize"
android:layout_alignParentBottom="true" />
android:id="@id/infoVersion"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:gravity="center"
android:ellipsize="end"
android:singleLine="true"
android:text="@string/title_version"
android:textSize="@dimen/info_label_textsize"
android:layout_alignParentBottom="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/kmp_browser_layout"
android:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".KMPBrowserActivity"
@ -9,6 +12,8 @@
<WebView
android:id="@+id/kmpBrowserWebView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
@ -9,27 +11,32 @@
android:background="@color/yellow_background"
tools:context=".MainActivity">
<include layout="@layout/titlebar" />
<include layout="@layout/titlebar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<include layout="@layout/check_chrome_webview_layout"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/titlebar"/>
<include
layout="@layout/check_chrome_webview_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/titlebar"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/titlebar" />
<com.keyman.engine.KMTextView
android:id="@+id/kmTextView"
android:background="@drawable/textview_bg"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/checkWebViewChromeLayout"
android:background="@drawable/textview_bg"
android:ems="10"
android:inputType="textMultiLine|textNoSuggestions"
android:hint="@string/textview_hint"
android:gravity="top"
android:scrollbars="vertical">
android:hint="@string/textview_hint"
android:inputType="textMultiLine|textNoSuggestions"
android:scrollbars="vertical"
app:layout_constraintTop_toBottomOf="@id/checkWebViewChromeLayout">
<requestFocus />
</com.keyman.engine.KMTextView>
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/package_installer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
@ -23,6 +24,17 @@
app:ms_nextButtonText="@string/label_next"
app:ms_completeButtonColor="@color/keyman_blue"
app:ms_completeButtonText="@string/label_ok"
app:ms_stepperType="dots" />
app:ms_stepperType="dots"
app:layout_constraintTop_toBottomOf="@+id/titlebar"
app:layout_constraintBottom_toTopOf="@+id/bottom_navigation"
app:layout_constraintStart_toStartOf="parent" />
</RelativeLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:fitsSystemWindows="true"
android:id="@+id/select_language_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
@ -15,7 +17,7 @@
android:id="@+id/stepperLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_below="@+id/titlebar"
app:ms_showBackButtonOnFirstStep="true"
app:ms_backButtonColor="@color/keyman_blue"
app:ms_backButtonText="@string/label_back"
@ -23,6 +25,18 @@
app:ms_nextButtonText="@string/label_next"
app:ms_completeButtonColor="@color/keyman_blue"
app:ms_completeButtonText="@string/label_ok"
app:ms_stepperType="dots" />
app:ms_stepperType="dots"
app:layout_constraintTop_toBottomOf="@id/titlebar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@+id/bottom_navigation" />
</RelativeLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintVertical_bias="1.0"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,23 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/keyman_settings_layout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
<androidx.appcompat.widget.Toolbar
android:id="@+id/keyman_settings_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:elevation="0dp">
<androidx.appcompat.widget.Toolbar
android:id="@+id/keyman_settings_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/keyman_blue">
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
android:layout_height="?attr/actionBarSize"
android:background="@color/keyman_blue"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<fragment
android:layout_marginTop="?attr/actionBarSize"
@ -26,4 +21,4 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -146,6 +146,12 @@
<!-- Context: Keyman Settings menu / Spacebar caption hint -->
<string name="spacebar_caption_hint_blank" comment="Spacebar caption hint - blank">Do not show caption on spacebar</string>
<!-- Context: Keyman Settings menu / Show OSK with Physical Keyboard -->
<string name="show_osk" comment="show OSK when physical keyboard is connected">Show On-Screen Keyboard</string>
<!-- Context: Keyman Settings menu / Show OSK with Physical Keyboard hint -->
<string name="show_osk_hint" comment="When physical keyboard is connected">When physical keyboard is connected</string>
<!-- Context: Keyman Settings menu / Haptic feedback -->
<string name="haptic_feedback" comment="haptic feedback on key press">Vibrate when typing</string>

View file

@ -12,8 +12,14 @@ base {
archivesName = "keyman-engine"
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
android {
compileSdk 34
compileSdk 35
namespace "com.keyman.engine"
buildFeatures {
@ -21,14 +27,9 @@ android {
buildConfig = true
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
defaultConfig {
minSdkVersion 21
targetSdkVersion 34
targetSdkVersion 35
// KEYMAN_VERSION_CODE and KEYMAN_VERSION_NAME from version.gradle but Gradle removes them for libraries
buildConfigField "String", "KEYMAN_ENGINE_VERSION_NAME", "\""+KEYMAN_VERSION_NAME+"\""
@ -65,10 +66,11 @@ android {
dependencies {
// 1.6.0-rc01 needed to resolve https://issuetracker.google.com/issues/238425626
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
// material:1.7.0 will need Gradle plugin 7.1.0+
implementation 'com.google.android.material:material:1.12.0'
implementation 'commons-io:commons-io:2.16.1'
implementation 'io.sentry:sentry-android:7.8.0'
implementation 'io.sentry:sentry-android:8.19.1'
implementation 'androidx.preference:preference:1.2.1'
// Robolectric

View file

@ -14,4 +14,10 @@ if(!Array.prototype.includes){
Array.prototype.includes = function(search){
return !!~this.indexOf(search);
}
}
// This file is only referenced by the main Web engine; its "global" object is `window`.
// `globalThis` is defined in Chrome 71+.
if(typeof globalThis == 'undefined') {
var globalThis = window;
}

View file

@ -9,10 +9,19 @@ import android.content.ContextWrapper;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.LocaleList;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsControllerCompat;
import androidx.preference.PreferenceManager;
import com.keyman.engine.util.ContextUtils;
@ -22,6 +31,7 @@ import java.util.Locale;
public class BaseActivity extends AppCompatActivity {
private static final String TAG = "BaseActivity";
static ContextWrapper localeUpdatedContext;
/**
@ -68,6 +78,58 @@ public class BaseActivity extends AppCompatActivity {
return "";
}
/**
* Setup the activity for edge-to-edge
* https://developer.android.com/develop/ui/views/layout/edge-to-edge-manually
* https://stackoverflow.com/questions/57293449/go-edge-to-edge-on-android-correctly-with-windowinsets
* @param layoutID - Top level ID of the view
*/
public void setupEdgeToEdge(int layoutID) {
EdgeToEdge.enable(this);
View anchor = findViewById(layoutID);
ViewCompat.setOnApplyWindowInsetsListener(anchor,
(view, windowInsets) -> {
// Allocate insets for system bars and display cutout (notch)
Insets insets = windowInsets.getInsets(
WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
view.setPadding(
insets.left,
insets.top,
insets.right,
insets.bottom);
KMManager.applyInsetsToKeyboard(
KMManager.KeyboardType.KEYBOARD_TYPE_INAPP, insets.left, insets.right, insets.bottom);
return windowInsets;
});
}
/**
* If Android API < 35, apply colors to the status bar and navigation bar
* @param statusBarcolor - int value of the color to use on the status bar.
* @param navigationBarColor - int value of the color to use on the navigation bar.
*/
public void setupStatusBarColors(int statusBarColor, int navigationBarColor) {
// Set status bar colors
// https://stackoverflow.com/a/79706054
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
WindowInsetsControllerCompat windowInsetsController =
WindowCompat.getInsetsController(window, window.getDecorView());
windowInsetsController.setAppearanceLightStatusBars(true);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
window.setStatusBarColor(getColor(statusBarColor));
window.setNavigationBarColor(getColor(navigationBarColor));
}
}
}
@Override
protected void attachBaseContext(Context newBase) {
// Override the app locale using the BCP 47 tag from shared preferences

View file

@ -64,6 +64,8 @@ public class KMHelpFileActivity extends BaseActivity {
}
setContentView(R.layout.activity_help_file_layout);
setupEdgeToEdge(R.id.help_file_layout);
setupStatusBarColors(android.R.color.white, android.R.color.white);
Bundle bundle = getIntent().getExtras();
pkgID = "";

View file

@ -52,6 +52,9 @@ import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import io.sentry.Breadcrumb;
import io.sentry.Sentry;
@ -495,6 +498,11 @@ public final class KMManager {
didCopyAssets = true;
}
calculateDefaultKeyboardHeights(context);
SharedPreferences prefs = context.getSharedPreferences(KMManager.KMEngine_PrefsKey, Context.MODE_PRIVATE);
KeyboardHeight_Context_Portrait_Current = prefs.getInt(KMManager.KMKey_KeyboardHeightPortrait, KMManager.KeyboardHeight_Context_Portrait_Default);
KeyboardHeight_Context_Landscape_Current = prefs.getInt(KMManager.KMKey_KeyboardHeightLandscape, KMManager.KeyboardHeight_Context_Landscape_Default);
if (keyboardType == KeyboardType.KEYBOARD_TYPE_UNDEFINED) {
String msg = "Cannot initialize: Invalid keyboard type";
KMLog.LogError(TAG, msg);
@ -508,12 +516,6 @@ public final class KMManager {
migrateCloudKeyboards(appContext);
CloudDownloadMgr.getInstance().initialize(appContext);
calculateDefaultKeyboardHeights(context);
SharedPreferences prefs = context.getSharedPreferences(KMManager.KMEngine_PrefsKey, Context.MODE_PRIVATE);
KeyboardHeight_Context_Portrait_Current = prefs.getInt(KMManager.KMKey_KeyboardHeightPortrait, KMManager.KeyboardHeight_Context_Portrait_Default);
KeyboardHeight_Context_Landscape_Current = prefs.getInt(KMManager.KMKey_KeyboardHeightLandscape, KMManager.KeyboardHeight_Context_Landscape_Default);
}
public static void executeResourceUpdate(Context aContext)
@ -692,15 +694,66 @@ public final class KMManager {
/**
* Adjust the keyboard dimensions. If the suggestion banner is active, use the
* combined banner height and keyboard height
*
* Android API 35+ enforces edge-to-edge so we need to determine the navigation bar height (if visible)
* so the keyboard isn't covered up by the navigation bar.
* In portrait orientation, this affects the keyboard bottom margin.
* On phones in landscape orientation, this affects the left/right side of the keyboard
* @return RelativeLayout.LayoutParams
*/
public static RelativeLayout.LayoutParams getKeyboardLayoutParams() {
int bannerHeight = getBannerHeight(appContext);
int kbHeight = getKeyboardHeight(appContext);
int navigationHeight = getNavigationBarHeight(appContext);
int orientation = getOrientation(appContext);
FormFactor formFactor = getFormFactor();
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT, bannerHeight + kbHeight);
params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
return params;
return params;
}
/**
* Applies padding to the keyboard so it's not covered by system insets
* @param {KeyboardType} keyboard
* @param left int padding to account for notch/navigation bar on left side of screen
* @param right int padding to account for notch/navigation bar on right side of screen
* @param bottom int padding to account for navigation bar on bottom of screen
*/
public static void applyInsetsToKeyboard(KeyboardType keyboardType, int left, int right, int bottom) {
if (keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP && InAppKeyboard != null) {
applyInsetsPaddingToKeyboardView(InAppKeyboard, left, right, bottom);
}
if (keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM && SystemKeyboard != null) {
applyInsetsPaddingToKeyboardView(SystemKeyboard, left, right, bottom);
}
}
private static void applyInsetsPaddingToKeyboardView(KMKeyboard keyboard, int left, int right, int bottom) {
View parent = (View) keyboard.getParent();
if (keyboard.keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP && parent != null) {
// Keep existing top padding, add bottom padding for the inset
parent.setPadding(
left,
parent.getPaddingTop(),
right,
bottom
);
// No need to change WebView margins; keep height as-is
parent.requestLayout();
} else {
// Fallback: apply on the WebView itself
keyboard.setPadding(
left,
keyboard.getPaddingTop(),
right,
bottom
);
keyboard.requestLayout();
}
}
private static void initKeyboard(Context appContext, KeyboardType keyboardType) {
@ -841,6 +894,25 @@ public final class KMManager {
return mainLayout;
}
if (mainLayout != null) {
// Update insets
ViewCompat.setOnApplyWindowInsetsListener(mainLayout,
(view, windowInsets) -> {
// Allocate insets for system bars and display cutout (notch)
Insets insets = windowInsets.getInsets(
WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
view.setPadding(
insets.left,
insets.top,
insets.right,
insets.bottom);
applyInsetsToKeyboard(KeyboardType.KEYBOARD_TYPE_SYSTEM, insets.left, insets.right, insets.bottom);
return windowInsets;
});
}
RelativeLayout keyboardLayout = new RelativeLayout(appContext);
keyboardLayout.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
ViewGroup parent = (ViewGroup) SystemKeyboard.getParent();
@ -2421,6 +2493,25 @@ public final class KMManager {
KeyboardHeight_Context_Landscape_Default = (int) newResources.getDimension(R.dimen.keyboard_height);
}
/**
* Get the navigation bar height (if visible) in pixels
* @param context - the context
* @return int - navigation bar height in pixels
*/
public static int getNavigationBarHeight(Context context) {
// Determine if navigation bar is visible
int resourceId = context.getResources().getIdentifier(
"config_showNavigationBar", "bool", "android");
if (resourceId <= 0) {
return 0;
}
// Navigation bar visible so get the height
resourceId = context.getResources().getIdentifier(
"navigation_bar_height", "dimen", "android");
return (resourceId > 0) ? context.getResources().getDimensionPixelSize(resourceId) : 0;
}
/**
* Get the size of the area the window would occupy.
* API 30+

View file

@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/help_file_layout"
android:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
@ -9,36 +12,28 @@
<WebView
android:id="@+id/kmHelpFileWebView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="0dp"
android:layout_below="@+id/titlebar"
android:layout_above="@+id/buttonBar" />
<LinearLayout
android:id="@+id/buttonBar"
style="?android:attr/buttonBarStyle"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<!-- Spacer to pad buttons to the right -->
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
app:layout_constraintTop_toBottomOf="@id/titlebar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@+id/finishButton" />
<Button
android:id="@+id/finishButton"
android:id="@id/finishButton"
style="@style/Widget.AppCompat.Button.Borderless.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_ok"
android:layout_marginStart="10dp"
android:layout_marginEnd="15dp"
android:layout_gravity="end"
android:elevation="1dp"
android:singleLine="true"
android:layout_marginStart="10dp" android:layout_marginEnd="15dp">
<requestFocus/>
android:text="@string/label_ok"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0">
<requestFocus />
</Button>
</LinearLayout>
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,13 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/activity_list_layout"
android:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/list_toolbar" />
<include layout="@layout/list_toolbar"
android:id="@+id/list_appbar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<include layout="@layout/list_layout" />
<include layout="@layout/list_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/list_appbar"
app:layout_constraintStart_toStartOf="parent" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,12 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/list_toolbar" />
<include layout="@layout/list_toolbar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<!-- ListView of installed keyboards. TODO: calculate height -->
<include
@ -14,45 +17,35 @@
layout="@layout/list_layout"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_above="@+id/keyboard_linear_layout"
android:layout_below="@id/list_appbar" />
android:layout_above="@+id/modeling_settings_layout"
android:layout_below="@id/list_appbar"
app:layout_constraintTop_toBottomOf="@id/list_appbar"/>
<LinearLayout
android:id="@+id/keyboard_linear_layout"
android:layout_width="match_parent"
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/add_button"
style="?android:attr/actionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/modeling_settings_layout"
android:orientation="horizontal">
<Space
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="10" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/add_button"
style="?android:attr/actionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:layout_marginTop="@dimen/fab_margin"
android:layout_marginBottom="@dimen/fab_margin"
android:layout_weight="1"
android:contentDescription="@string/image_button"
android:src="@drawable/ic_add_white"
app:fabSize="normal" />
</LinearLayout>
android:layout_gravity="end|center_vertical"
android:contentDescription="@string/image_button"
android:src="@drawable/ic_add_white"
android:layout_marginEnd="@dimen/fab_margin"
android:layout_marginBottom="@dimen/fab_margin"
app:fabSize="normal"
app:layout_constraintBottom_toTopOf="@+id/modeling_settings_layout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"/>
<LinearLayout
android:id="@+id/modeling_settings_layout"
android:id="@id/modeling_settings_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginTop="0dp"
android:layout_marginBottom="0dp"
android:orientation="vertical">
android:orientation="vertical"
app:layout_constraintVertical_bias="1.0"
app:layout_constraintBottom_toBottomOf="parent">
<View
android:id="@+id/separator"
@ -117,4 +110,4 @@
android:layout_height="wrap_content"
android:layout_below="@id/model_picker"/> -->
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,31 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/languages_list_layout"
android:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/list_toolbar" />
<include layout="@layout/list_toolbar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<include
android:id="@+id/keyboard_list_layout"
layout="@layout/list_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/list_appbar" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="horizontal">
<Space
android:layout_width="wrap_content"
<include
android:id="@+id/keyboard_list_layout"
layout="@layout/list_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="10" />
android:layout_below="@+id/list_appbar"
app:layout_constraintTop_toBottomOf="@id/list_appbar"
app:layout_constraintStart_toStartOf="parent"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/add_button"
@ -33,12 +27,13 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:layout_marginTop="@dimen/fab_margin"
android:layout_marginBottom="@dimen/fab_margin"
android:layout_weight="1"
android:contentDescription="@string/image_button"
android:src="@drawable/ic_add_white"
app:fabSize="normal" />
android:layout_marginEnd="@dimen/fab_margin"
android:layout_marginBottom="@dimen/fab_margin"
app:fabSize="normal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"/>
</LinearLayout>
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,15 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- paddingBottom so last entry doesn't get covered by navigation bar -->
<ListView
style="@style/AppTheme.Light.ListView"
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:choiceMode="singleChoice"
android:clipToPadding="false" />
android:clipToPadding="false"
android:paddingBottom="?attr/actionBarSize"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
</RelativeLayout>

View file

@ -1,16 +1,20 @@
<com.google.android.material.appbar.AppBarLayout
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.appbar.AppBarLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/list_appbar"
app:theme="@style/AppTheme.ToolbarBackArrow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:elevation="0dp">
<androidx.appcompat.widget.Toolbar
android:id="@+id/list_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize">
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/bar_title"
@ -32,6 +36,8 @@
style="@style/AppTheme.Divider"
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_alignParentBottom="true"/>
app:layout_constraintTop_toBottomOf="@id/list_toolbar"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</com.google.android.material.appbar.AppBarLayout>

View file

@ -6,7 +6,9 @@
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_alignParentTop="true"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent">
<TextView
android:id="@id/bar_title"

View file

@ -83,13 +83,13 @@
<!-- 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 requires WebView to be installed.\"</string>
<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 -->
<string name="body_enable_webview" comment="Notification when WebView installed but not enabled"> Keyman requires WebView to be enabled.\"</string>
<string name="body_enable_webview" comment="Notification when WebView installed but not enabled"> Keyman requiere que WebView esté habilitado.\"</string>
<!-- Context Button label: Install WebView -->
<string name="label_install_webview" comment="Button label to install webview">Install WebView</string>
<string name="label_install_webview" comment="Button label to install webview">Instalar WebView</string>
<!-- Context Button label: Enable WebView -->
<string name="label_enable_webview" comment="Button label to enable webview">Enable WebView</string>
<string name="label_enable_webview" comment="Button label to enable webview">Habilitar WebView</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Comprobando si hay un diccionario asociado para descargar</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> No se puede conectar al servidor Keyman para comprobar si hay un diccionario asociado para descargar</string>
@ -124,7 +124,7 @@
<!-- Context: General Updates -->
<string name="update_check_unavailable" comment="Error message when a Keyman server can't be reached">¡Falló al acceder al servidor!</string>
<!-- Context: Update-check failure notification -->
<string name="update_check_download_manager_disabled" comment="Notification that a system service needed for updates is disabled">DownloadManager disabled - cannot check for updates</string>
<string name="update_check_download_manager_disabled" comment="Notification that a system service needed for updates is disabled">Descargador deshabilitado: no se pueden comprobar actualizaciones</string>
<!-- Context: General Updates -->
<string name="update_check_current" comment="Notification that all resources are up to date">"¡Todos los recursos están actualizados!"</string>
<!-- Context: General Updates -->

View file

@ -7,7 +7,6 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:8.9.2'
// io.sentry:sentry-android-gradle-plugin not available for library project
classpath 'io.sentry:sentry-android:7.8.0'
classpath 'name.remal:gradle-plugins:1.9.2'
}
}

View file

@ -4,11 +4,10 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../resources/build/builder.inc.sh"
. "${THIS_SCRIPT%/*}/../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
. "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh"
. "$KEYMAN_ROOT/resources/build/utils.inc.sh"
# ################################ Main script ################################

View file

@ -34,10 +34,10 @@ analytics for Debug are associated with an App Bundle ID
### Compiling From Command Line
1. Launch a command prompt and cd to the directory **keyman/android**
2. Run the top level build script `./build.sh configure build:engine build:app --debug` which will:
* Compile KMEA (and its KMW dependency)
2. Run the top level build script `./build.sh configure build:engine build:app` which will:
* Compile debug version of KMEA (and its KMW dependency)
* Download default keyboard and dictionary resources as needed
* Compile KMAPro
* Compile debug version of KMAPro
3. The APK will be found in **keyman/android/KMAPro/kMAPro/build/outputs/apk/debug/keyman-\${version}.apk**
where `${version}` is the current version number.
@ -98,10 +98,10 @@ There are two included sample projects that can be modified to test a keyboard.
**android/Samples/KMSample2** app provides prompts for setting KMSample2 as a system level keyboard.
Both sample apps include a default Tamil keyboard and sample dictionary.
Building these projects follow the same steps as KMAPro:
Building the debug versions of these projects follow the same steps as KMAPro:
1. cd to the desired KMSample directory
2. `./build.sh configure build --debug`
2. `./build.sh configure build`
3. Open Android Studio to run the app
### Tests: KeyboardHarness
@ -114,14 +114,14 @@ Building these projects follow the same steps as KMAPro:
* Build the keyboardharness.kmp keyboard package
3. Add the keyboard in *android/Tests/KeyboardHarness/app/src/main/java/com/keyman/android/tests/keyboardHarness/MainActivity.java*
4. cd to android/Tests/KeyboardHarness/
5. `./build.sh configure build --debug`
5. `./build.sh configure build`
6. Open Android Studio to run the app
--------------------------------------------------------------
## How to Build Keyman Engine for Android
1. Open a terminal or Git Bash prompt and go to the Android project folder (e.g. `cd ~/keyman/android/`)
2. Run `./build.sh build:engine --debug`
2. Run `./build.sh build:engine`
Keyman Engine for Android library (**keyman-engine.aar**) is now ready to be imported in any project.
@ -135,7 +135,7 @@ Keyman Engine for Android library (**keyman-engine.aar**) is now ready to be imp
4. Check that the `android{}` object, includes the following:
```gradle
android {
compileSdkVersion 34
compileSdkVersion 35
// Don't compress kmp files so they can be copied via AssetManager
aaptOptions {
@ -159,6 +159,7 @@ repositories {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
implementation 'com.google.android.material:material:1.12.0'
api (name:'keyman-engine', ext:'aar')
implementation 'androidx.preference:preference:1.2.1'

View file

@ -2,8 +2,14 @@ plugins {
id 'com.android.application'
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
android {
compileSdk 34
compileSdk 35
namespace="com.keyman.kmsample1"
// Don't compress kmp files so they can be copied via AssetManager
@ -14,7 +20,7 @@ android {
defaultConfig {
applicationId "com.keyman.kmsample1"
minSdkVersion 21
targetSdkVersion 34
targetSdkVersion 35
versionCode 1
versionName "1.0"
}
@ -24,12 +30,15 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
repositories {
flatDir {
dirs 'libs'
}
google()
}
@ -37,7 +46,8 @@ repositories {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
implementation 'com.google.android.material:material:1.12.0'
api(name: 'keyman-engine', ext: 'aar')
implementation files('libs/keyman-engine.aar')
implementation 'androidx.preference:preference:1.2.1'
}

View file

@ -1,13 +1,16 @@
package com.keyman.kmsample1;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Point;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.Menu;
import android.view.MenuItem;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.keyman.engine.BaseActivity;
import com.keyman.engine.data.Keyboard;
import com.keyman.engine.KMKeyboardDownloaderActivity;
import com.keyman.engine.KMManager;
@ -19,10 +22,12 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MainActivity extends AppCompatActivity implements OnKeyboardEventListener, OnKeyboardDownloadEventListener {
public class MainActivity extends BaseActivity implements OnKeyboardEventListener, OnKeyboardDownloadEventListener {
public static Context context;
private ConstraintLayout constraintLayout;
private KMTextView textView;
private int lastOrientation = Configuration.ORIENTATION_UNDEFINED;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -34,6 +39,12 @@ public class MainActivity extends AppCompatActivity implements OnKeyboardEventLi
KMManager.initialize(this, KeyboardType.KEYBOARD_TYPE_INAPP);
setContentView(R.layout.activity_main);
constraintLayout = findViewById(R.id.constraintLayout);
setupEdgeToEdge(R.id.constraintLayout);
setupStatusBarColors(
android.R.color.white, // Color for top status bar
android.R.color.darker_gray); // Color for bottom navigation bar
textView = (KMTextView) findViewById(R.id.kmTextView);
// Add a custom keyboard
@ -91,6 +102,18 @@ public class MainActivity extends AppCompatActivity implements OnKeyboardEventLi
protected void onResume() {
super.onResume();
KMManager.onResume();
// onConfigurationChanged() only triggers when device is rotated while app is in foreground
// This handles when device is rotated while app is in background
// using KMManager.getOrientation() since getConfiguration().orientation is unreliable #10241
int newOrientation = KMManager.getOrientation(context);
if (newOrientation != lastOrientation) {
lastOrientation = newOrientation;
Configuration newConfig = this.getResources().getConfiguration();
KMManager.onConfigurationChanged(newConfig);
}
resizeTextView(textView.isKeyboardVisible());
KMManager.addKeyboardEventListener(this);
KMKeyboardDownloaderActivity.addKeyboardDownloadEventListener(this);
}
@ -108,6 +131,10 @@ public class MainActivity extends AppCompatActivity implements OnKeyboardEventLi
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
KMManager.onConfigurationChanged(newConfig);
resizeTextView(textView.isKeyboardVisible());
lastOrientation = newConfig.orientation;
}
@Override
@ -123,11 +150,44 @@ public class MainActivity extends AppCompatActivity implements OnKeyboardEventLi
@Override
public void onKeyboardShown() {
// Handle Keyman keyboard shown event here if needed
resizeTextView(true);
}
@Override
public void onKeyboardDismissed() {
// Handle Keyman keyboard dismissed event here if needed
resizeTextView(false);
}
public void resizeTextView(boolean isKeyboardVisible) {
int bannerHeight = 0;
int keyboardHeight = 0;
if (isKeyboardVisible) {
bannerHeight = KMManager.getBannerHeight(this);
keyboardHeight = KMManager.getKeyboardHeight(this);
}
TypedValue outValue = new TypedValue();
getTheme().resolveAttribute(android.R.attr.actionBarSize, outValue, true);
int actionBarHeight = getResources().getDimensionPixelSize(outValue.resourceId);
// *** TO DO: Try to check if status bar is visible, set statusBarHeight to 0 if it is not visible ***
int statusBarHeight = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0)
statusBarHeight = getResources().getDimensionPixelSize(resourceId);
// Navigation bar height
int navigationBarHeight = 0;
resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
navigationBarHeight = getResources().getDimensionPixelSize(resourceId);
}
Point size = KMManager.getWindowSize(context);
int screenHeight = size.y;
textView.setHeight(
screenHeight - statusBarHeight - actionBarHeight - bannerHeight - keyboardHeight - navigationBarHeight);
}
@Override

View file

@ -1,7 +1,11 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical"
tools:context=".MainActivity">
<com.keyman.engine.KMTextView
@ -12,8 +16,10 @@
android:ems="10"
android:inputType="textMultiLine"
android:gravity="top"
android:scrollbars="vertical" >
android:scrollbars="vertical"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent">
<requestFocus />
</com.keyman.engine.KMTextView>
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -4,11 +4,10 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
. "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh"
. "$KEYMAN_ROOT/resources/build/utils.inc.sh"
################################ Main script ################################
@ -23,7 +22,7 @@ builder_describe "Build KMSample1 app for Android." \
"configure" \
"build" \
"test" \
":app KMSample1"
":app KMSample1"
# parse before describe_outputs to check debug flags
builder_parse "$@"
@ -39,7 +38,6 @@ fi
builder_describe_outputs \
configure /android/Samples/KMSample1/app/libs/keyman-engine.aar \
build:app /android/Samples/KMSample1/app/build/outputs/apk/$CONFIG/$ARTIFACT

View file

@ -2,8 +2,14 @@ plugins {
id 'com.android.application'
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
android {
compileSdk 34
compileSdk 35
namespace="com.keyman.kmsample2"
// Don't compress kmp files so they can be copied via AssetManager
@ -14,7 +20,7 @@ android {
defaultConfig {
applicationId "com.keyman.kmsample2"
minSdkVersion 21
targetSdkVersion 34
targetSdkVersion 35
versionCode 1
versionName "1.0"
}
@ -24,19 +30,23 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
repositories {
flatDir {
dirs 'libs'
}
google()
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
implementation 'com.google.android.material:material:1.12.0'
api (name:'keyman-engine', ext:'aar')
implementation files('libs/keyman-engine.aar')
implementation 'androidx.preference:preference:1.2.1'
}

View file

@ -1,6 +1,5 @@
package com.keyman.kmsample2;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.content.Intent;
import android.provider.Settings;
@ -11,13 +10,20 @@ import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
import com.keyman.engine.BaseActivity;
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupEdgeToEdge(R.id.constraintLayout);
setupStatusBarColors( // Inset colors for Android API < 35
android.R.color.black, // Color for status bar on top
android.R.color.white // Color for navigation bar on bottom
);
Button button1 = (Button) findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {

View file

@ -184,12 +184,14 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
Point size = KMManager.getWindowSize(getApplicationContext());
int inputViewHeight = 0;
if (inputView != null)
if (inputView != null) {
inputViewHeight = inputView.getHeight();
}
int navigationHeight = KMManager.getNavigationBarHeight(this);
int bannerHeight = KMManager.getBannerHeight(this);
int kbHeight = KMManager.getKeyboardHeight(this);
outInsets.contentTopInsets = inputViewHeight - bannerHeight - kbHeight;
outInsets.contentTopInsets = inputViewHeight - bannerHeight - kbHeight - navigationHeight;
outInsets.visibleTopInsets = outInsets.contentTopInsets;
outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
outInsets.touchableRegion.set(0, outInsets.contentTopInsets, size.x, size.y);
@ -215,6 +217,20 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
// Handle Keyman keyboard dismissed event here if needed
}
@Override
public boolean onEvaluateInputViewShown() {
// On Android API 36+, the OSK defaults to not appearing when a physical keyboard is connected.
// If the default implementation returns true, recommend honoring it
// Reference: https://android.googlesource.com/platform/frameworks/base/+/7b739a8%5E%21/
if (super.onEvaluateInputViewShown()) {
return true;
};
// TODO: Implement IME setting similar to Gboard "Show on-screen keyboard" with physical keyboard
// (default false)
return true;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {

View file

@ -1,31 +1,50 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<TextView android:text="@string/description" android:layout_width="wrap_content"
android:layout_height="wrap_content"
<TextView
android:id="@+id/textView"
android:layout_centerHorizontal="true" />
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/description"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:layout_width="260dp"
android:layout_height="wrap_content"
android:text="Open input method settings"
android:id="@+id/button1"
android:layout_below="@+id/textView"
android:layout_centerHorizontal="true"
android:layout_marginTop="16dp" />
<Button
android:layout_width="260dp"
android:layout_height="wrap_content"
android:text="Open input method menu"
android:id="@+id/button2"
android:layout_below="@+id/button1"
android:layout_centerHorizontal="true"
android:layout_marginTop="16dp" />
android:layout_below="@+id/textView"
android:layout_centerInParent="true"
android:layout_marginTop="16dp"
android:text="Open input method settings"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textView" />
</RelativeLayout>
<Button
android:id="@+id/button2"
android:layout_width="260dp"
android:layout_height="wrap_content"
android:layout_below="@+id/button1"
android:layout_marginTop="16dp"
android:text="Open input method menu"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/button1" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -4,11 +4,10 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
. "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh"
. "$KEYMAN_ROOT/resources/build/utils.inc.sh"
################################ Main script ################################
@ -23,7 +22,7 @@ builder_describe "Build KMSample2 app for Android." \
"configure" \
"build" \
"test" \
":app KMSample2"
":app KMSample2"
# parse before describe_outputs to check debug flags
builder_parse "$@"
@ -40,7 +39,6 @@ fi
builder_describe_outputs \
configure /android/Samples/KMSample2/app/libs/keyman-engine.aar \
build:app /android/Samples/KMSample2/app/build/outputs/apk/$CONFIG/$ARTIFACT

View file

@ -5,8 +5,14 @@ plugins {
ext.rootPath = '../../../'
apply from: "$rootPath/version.gradle"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
android {
compileSdk 34
compileSdk 35
namespace="com.keyman.android.tests.keyboardHarness"
// Don't compress kmp files so they can be copied via AssetManager
@ -21,7 +27,7 @@ android {
defaultConfig {
applicationId "com.keyman.android.tests.keyboardHarness"
minSdkVersion 21
targetSdkVersion 34
targetSdkVersion 35
// KEYMAN_VERSION_CODE and KEYMAN_VERSION_NAME from version.gradle
versionCode KEYMAN_VERSION_CODE as Integer
@ -34,12 +40,15 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
repositories {
flatDir {
dirs 'libs'
}
google()
mavenCentral()
}
@ -47,7 +56,8 @@ repositories {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
implementation 'com.google.android.material:material:1.12.0'
api (name:'keyman-engine', ext:'aar')
implementation files('libs/keyman-engine.aar')
implementation 'androidx.preference:preference:1.2.1'
}

View file

@ -1,11 +1,13 @@
package com.keyman.android.tests.keyboardHarness;
import androidx.appcompat.app.AppCompatActivity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.keyman.engine.BaseActivity;
import com.keyman.engine.data.Keyboard;
import com.keyman.engine.KMKeyboardDownloaderActivity;
import com.keyman.engine.KMManager;
@ -18,8 +20,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MainActivity extends AppCompatActivity implements OnKeyboardEventListener, OnKeyboardDownloadEventListener {
public class MainActivity extends BaseActivity implements OnKeyboardEventListener, OnKeyboardDownloadEventListener {
private ConstraintLayout constraintLayout;
private KMTextView textView;
@Override
@ -31,6 +34,12 @@ public class MainActivity extends AppCompatActivity implements OnKeyboardEventLi
KMManager.initialize(this, KeyboardType.KEYBOARD_TYPE_INAPP);
setContentView(R.layout.activity_main);
constraintLayout = findViewById(R.id.constraintLayout);
setupEdgeToEdge(R.id.constraintLayout);
setupStatusBarColors(
android.R.color.white, // Color for top status bar
android.R.color.darker_gray); // Color for bottom navigation bar
textView = (KMTextView) findViewById(R.id.kmTextView);
// Add custom keyboards

View file

@ -1,7 +1,11 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical"
tools:context=".MainActivity">
<com.keyman.engine.KMTextView
@ -12,9 +16,11 @@
android:ems="10"
android:inputType="textMultiLine"
android:gravity="top"
android:scrollbars="vertical" >
android:scrollbars="vertical"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent">
<requestFocus />
</com.keyman.engine.KMTextView>
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -15,8 +15,8 @@ buildscript {
allprojects {
repositories {
maven { url uri("${projectDir}/libs") }
google()
jcenter()
mavenCentral()
}
}

View file

@ -4,11 +4,10 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
. "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh"
. "$KEYMAN_ROOT/resources/build/utils.inc.sh"
################################ Main script ################################
@ -23,7 +22,7 @@ builder_describe "Build KeyboardHarness test app for Android." \
"configure" \
"build" \
"test" \
":app KeyboardHarness"
":app KeyboardHarness"
# parse before describe outputs to check debug flags
builder_parse "$@"
@ -40,7 +39,6 @@ fi
builder_describe_outputs \
configure /android/Tests/KeyboardHarness/app/libs/keyman-engine.aar \
build:app /android/Tests/KeyboardHarness/app/build/outputs/apk/$CONFIG/${ARTIFACT}
#### Build

View file

@ -1,16 +1,23 @@
apply plugin: 'com.android.application'
plugins {
id 'com.android.application'
}
ext.rootPath = '../../../'
apply from: "$rootPath/version.gradle"
android {
compileSdk 34
compileSdk 35
namespace="com.keyman.android.tests.keycode"
buildFeatures {
// needed for custom BuildConfigField values in defaultConfig
buildConfig = true
}
defaultConfig {
applicationId "com.keyman.android.tests.keycode"
minSdkVersion 21
targetSdkVersion 34
targetSdkVersion 35
// KEYMAN_VERSION_CODE and KEYMAN_VERSION_NAME from version.gradle
versionCode KEYMAN_VERSION_CODE as Integer
@ -27,11 +34,10 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
implementation 'com.google.android.material:material:1.12.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'androidx.test:core:1.5.0'
testImplementation 'androidx.test.ext:junit:1.1.5'
testImplementation 'org.robolectric:robolectric:4.10.3'
testImplementation 'androidx.test.ext:junit:1.2.1'
testImplementation 'org.robolectric:robolectric:4.14.1'
testImplementation 'androidx.test:core:1.6.1'
}

View file

@ -1,25 +1,25 @@
package com.keyman.android.tests.keycode;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Test;
import org.junit.runner.RunWith;
import android.content.Context;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
@RunWith(RobolectricTestRunner.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
Context appContext = ApplicationProvider.getApplicationContext();
assertEquals("com.keyman.android.tests.keycode", appContext.getPackageName());
}

View file

@ -1,4 +1,4 @@
package com.example.darcy.keycode;
package com.keyman.android.tests.keycode;
import org.junit.Test;

View file

@ -4,10 +4,10 @@ buildscript {
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.4.2'
classpath 'com.android.tools.build:gradle:8.9.2'
// NOTE: Do not place your application dependencies here; they belong
@ -18,10 +18,6 @@ buildscript {
allprojects {
repositories {
google()
jcenter()
mavenCentral()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View file

@ -14,4 +14,6 @@ org.gradle.jvmargs=-Xmx1024m
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AGP 8.0 generates R classes for resources defined in the current module only.
android.nonTransitiveRClass=false

View file

@ -10,12 +10,11 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../resources/build/builder.inc.sh"
. "${THIS_SCRIPT%/*}/../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
# shellcheck disable=SC2154
. "${KEYMAN_ROOT}/resources/shellHelperFunctions.sh"
. "${KEYMAN_ROOT}/resources/build/build-utils-ci.inc.sh"
. "${KEYMAN_ROOT}/resources/build/utils.inc.sh"
. "${KEYMAN_ROOT}/resources/build/zip.inc.sh"
################################ Main script ################################
@ -38,9 +37,9 @@ builder_describe \
configure \
build \
test \
"publish Publishes symbols to Sentry and the APKs to the Play Store." \
"publish-symbols Publishes symbols to Sentry" \
"publish-play-store Publishes APKs to the Play Store" \
"archive Copy release artifacts to upload/ and create zip" \
--upload-sentry+ \
":engine=KMEA Keyman Engine for Android" \
":app=KMAPro Keyman for Android" \
":help Online documentation" \
@ -63,11 +62,12 @@ 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"
KEYMAN_APK="keyman-${KEYMAN_VERSION}.apk"
FIRSTVOICES_APK="firstvoices-${KEYMAN_VERSION}.apk"
KEYMAN_APK="keyman-${KEYMAN_VERSION_FOR_FILENAME}.apk"
FIRSTVOICES_APK="firstvoices-${KEYMAN_VERSION_FOR_FILENAME}.apk"
rm -rf "${UPLOAD_PATH}"
mkdir -p "${UPLOAD_PATH}"
@ -88,12 +88,12 @@ function archive_artifacts() {
)
# Copy release APK
cp "${KEYMAN_ROOT}/android/KMAPro/kMAPro/build/outputs/apk/release/${KEYMAN_APK}" "${UPLOAD_PATH}"
cp "${KEYMAN_ROOT}/android/KMAPro/kMAPro/build/outputs/apk/release/${KEYMAN_APK}" "${UPLOAD_PATH}/${KEYMAN_APK}"
# FirstVoices app
if [[ "${RELEASE_OEM_FIRSTVOICES-false}" = true ]]; then
cp "${KEYMAN_ROOT}/oem/firstvoices/android/app/build/outputs/apk/release/${FIRSTVOICES_APK}" "${UPLOAD_PATH}"
cp "${KEYMAN_ROOT}/oem/firstvoices/android/app/build/outputs/apk/release/${FIRSTVOICES_APK}" "${UPLOAD_PATH}/${FIRSTVOICES_APK}"
fi
#
@ -108,8 +108,18 @@ function archive_artifacts() {
fi
}
# Override JAVA_HOME to OpenJDK 11
set_java_home
# For CI compatibility of building Keyman for Android 18.0 with OpenJDK 11,
# this overrides JAVA_HOME for the builder script to use OpenJDK 21.
android_set_java_home() {
if [[ ! -z ${JAVA_HOME_21+x} ]]; then
builder_echo "Setting JAVA_HOME to JAVA_HOME_21 (${JAVA_HOME_21})"
export JAVA_HOME="${JAVA_HOME_21}"
fi
}
# Override JAVA_HOME
android_set_java_home
# This script also responsible for cleaning up /android/upload
builder_run_child_actions clean
@ -120,6 +130,6 @@ builder_run_child_actions configure build test
builder_run_action test:help do_test_help
builder_run_child_actions publish
builder_run_child_actions publish-symbols publish-play-store
builder_run_action archive archive_artifacts

View file

@ -0,0 +1,42 @@
---
title: KMManager.getNavigationBarHeight()
---
## Summary
The **`getNavigationBarHeight()`** method returns the height of the navigation bar.
## Syntax
``` javascript
KMManager.getNavigationBarHeight(Context context)
```
### Parameters
`context`
: The context.
### Returns
Returns the height of the navigation bar in pixels.
## Description
Use this method to get the height of the navigation bar. The keyboard offsets must account for this height below the keyboard.
## Examples
### Example: Using `getNavigationBarHeight()`
The following script illustrate the use of `getNavigationBarHeight()`:
``` javascript
int navigationHeight = KMManager.getNavigationBarHeight(this);
```
## See Also
- [`getBannerHeight()`](getBannerHeight)
- [`getKeyboardHeight()`](getKeyboardHeight)

View file

@ -149,6 +149,9 @@ The KMManager is the core class which provides most of the methods and constants
[`getMaySendCrashReport()`](getMaySendCrashReport)
: returns whether Keyman Engine is allowed to send crash reports over the network to Sentry
[`getNavigationBarHeight()`](getNavigationBarHeight)
: returns the height of the navigation bar system inset
[`getOrientation()`](getOrientation)
: returns the device's current orientation (Portrait vs Landscape)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View file

@ -49,6 +49,9 @@ Click on this to [adjust the keyboard height](adjust-keyboard-height).
Click on this to [adjust the delay time](adjust-longpress-delay) for a longpress gesture.
## Show On-Screen Keyboard
By default, Android 16 (Baklava) and later versions won't display the On-Screen keyboard if a physical keyboard is connected. When enabled, show the Keyman system keyboard even if a physical keyboard is connected.
## Spacebar Caption
Click on this to [change the displayed keyboard name](spacebar-caption) on the spacebar.

View file

@ -25,7 +25,7 @@ set -u # die on undefined variables
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh"
. "${THIS_SCRIPT%/*}/../../resources/build/builder-basic.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
# Preparation

View file

@ -2,7 +2,7 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/resources/build/builder.inc.sh"
. "${THIS_SCRIPT%/*}/resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
builder_describe "Keyman -- all projects" \

View file

@ -2,7 +2,7 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../resources/build/builder.inc.sh"
. "${THIS_SCRIPT%/*}/../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
builder_describe "Keyman common and resources modules" \

View file

@ -5,7 +5,7 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../resources/build/builder.inc.sh"
. "${THIS_SCRIPT%/*}/../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
builder_describe "Build keymanversion_build.h" configure build clean

View file

@ -2,7 +2,7 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../resources/build/builder.inc.sh"
. "${THIS_SCRIPT%/*}/../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
#

View file

@ -2,7 +2,7 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../resources/build/builder.inc.sh"
. "${THIS_SCRIPT%/*}/../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
#

View file

@ -2,7 +2,7 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../../resources/build/builder.inc.sh"
. "${THIS_SCRIPT%/*}/../../../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
builder_describe "Baseline keyboards tests -- built with 16.0 compiler" \

View file

@ -2,7 +2,7 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
. "$KEYMAN_ROOT/resources/build/zip.inc.sh"

View file

@ -3,8 +3,8 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../../../resources/build/build-utils.sh"
# . "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-basic.inc.sh"
# . "$KEYMAN_ROOT/resources/build/utils.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
THIS_DIR="$(dirname "$THIS_SCRIPT")"

View file

@ -19,7 +19,9 @@ import { fileURLToPath } from 'url';
/**
* Creates a MessageEvent (for inter-worker communication), with the given data payload.
*
* @param {*} data
* @template {T}
* @param {T} data
* @returns {T}
*/
export function createMessageEventWithData(data) {
return { data };

View file

@ -8,7 +8,7 @@ set -eu
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../resources/build/build-utils.sh"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder-basic.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE

View file

@ -2,10 +2,11 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
. "$KEYMAN_ROOT/resources/build/utils.inc.sh"
. "$KEYMAN_ROOT/resources/build/node.inc.sh"
################################ Main script ################################
@ -17,5 +18,5 @@ builder_describe_outputs \
builder_parse "$@"
builder_run_action clean rm -rf build/ node_modules/
builder_run_action configure verify_npm_setup
builder_run_action configure node_select_version_and_npm_ci
builder_run_action build tsc --build

View file

@ -6,10 +6,11 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
. "$KEYMAN_ROOT/resources/build/utils.inc.sh"
. "$KEYMAN_ROOT/resources/build/node.inc.sh"
################################ Main script ################################
@ -34,7 +35,7 @@ fi
### CONFIGURE ACTIONS
if builder_start_action configure; then
verify_npm_setup
node_select_version_and_npm_ci
builder_finish_action success configure
fi

View file

@ -5,7 +5,7 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../resources/build/builder.inc.sh"
. "${THIS_SCRIPT%/*}/../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
builder_describe "Keyman common web modules" \

View file

@ -2,11 +2,12 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
. "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh"
. "$KEYMAN_ROOT/resources/build/utils.inc.sh"
. "$KEYMAN_ROOT/resources/build/node.inc.sh"
. "$KEYMAN_ROOT/resources/build/ci/ci-publish.inc.sh"
################################ Main script ################################
@ -53,6 +54,6 @@ export default KEYMAN_VERSION;
}
builder_run_action clean rm -rf version.inc.ts keyman-version.mts build/
builder_run_action configure verify_npm_setup
builder_run_action configure node_select_version_and_npm_ci
builder_run_action build do_build
builder_run_action publish builder_publish_npm
builder_run_action publish ci_publish_npm

View file

@ -2,11 +2,12 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
. "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh"
. "$KEYMAN_ROOT/resources/build/utils.inc.sh"
. "$KEYMAN_ROOT/resources/build/node.inc.sh"
. "$KEYMAN_ROOT/resources/build/ci/ci-publish.inc.sh"
builder_describe "Build Keyman langtags.js common module" \
"clean" \
@ -32,7 +33,7 @@ function compile_langtags() {
}
function do_configure() {
verify_npm_setup
node_select_version_and_npm_ci
compile_langtags
}
@ -42,4 +43,4 @@ builder_run_action clean rm -rf ./build ./src/imports ./node_modules
builder_run_action configure do_configure
builder_run_action build tsc --build
builder_run_action test echo 'no tests for langtags'
builder_run_action publish builder_publish_npm
builder_run_action publish ci_publish_npm

View file

@ -2,11 +2,12 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
. "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh"
. "$KEYMAN_ROOT/resources/build/utils.inc.sh"
. "$KEYMAN_ROOT/resources/build/node.inc.sh"
. "$KEYMAN_ROOT/resources/build/ci/ci-publish.inc.sh"
builder_describe "Build Keyman common file types module" \
"@/core/include/ldml" \
@ -76,7 +77,7 @@ function compile_schemas() {
function do_configure() {
compile_schemas
verify_npm_setup
node_select_version_and_npm_ci
}
function do_test() {
@ -104,4 +105,4 @@ builder_run_action clean rm -rf ./build/ ./tsconfig.tsbuildinfo ./src/schem
builder_run_action configure do_configure
builder_run_action build tsc --build
builder_run_action test do_test
builder_run_action publish builder_publish_npm
builder_run_action publish ci_publish_npm

View file

@ -417,13 +417,24 @@ export interface ProbabilityMass<T> {
export type Distribution<T> = ProbabilityMass<T>[];
/**
* A type augmented with an optional probability.
* A type augmented with optional probability data.
*/
export type Outcome<T> = T & {
/**
* [optional] probability of this outcome.
* [optional] the modeled likelihood associated with this outcome.
*/
p?: number;
/**
* The likelihood of the suggestion itself based solely on the lexical model
*/
['lexical-p']?: number
/**
* The likelihood associated with the keystroke sequence and/or associated
* text corrections best matching the suggestion.
*/
['correction-p']?: number
};
/**
@ -434,6 +445,21 @@ export type WithOutcome<T> = T & {
* Probability of this outcome.
*/
p: number;
/**
* The likelihood of the suggestion itself based solely on the lexical model
*
* Only emitted for verbose mode.
*/
['lexical-p']?: number
/**
* The likelihood associated with the keystroke sequence and/or associated
* text corrections best matching the suggestion.
*
* Only omitted for verbose mode.
*/
['correction-p']?: number
};

View file

@ -2,7 +2,7 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../resources/build/builder.inc.sh"
. "${THIS_SCRIPT%/*}/../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
#

View file

@ -13,8 +13,8 @@ set -u
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh"
# . "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
. "${THIS_SCRIPT%/*}/../../resources/build/builder-basic.inc.sh"
# . "$KEYMAN_ROOT/resources/build/utils.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
pushd $KEYMAN_CEF4DELPHI_ROOT > /dev/null

View file

@ -2,7 +2,7 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
builder_describe \

View file

@ -2,7 +2,7 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
builder_describe \

View file

@ -2,7 +2,7 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../../resources/build/builder.inc.sh"
. "${THIS_SCRIPT%/*}/../../../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
builder_describe "Common Delphi components" clean configure build test edit

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