Merge branch 'feat/developer/kmc-convert-createSeperateUtil' of https://github.com/keymanapp/keyman into feat/developer/kmc-convert-createSeperateUtil

This commit is contained in:
Sabine 2026-02-02 13:51:38 +01:00
commit 8cffd1b18d
623 changed files with 28509 additions and 13934 deletions

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

@ -13,7 +13,7 @@ on:
jobs:
build:
runs-on: ubuntu-latest
if: ${{ github.repository == 'keymanapp/keyman' && github.actor == 'keyman-server' && startsWith(github.event.pull_request.title, 'auto:') }}
if: ${{ github.repository == 'keymanapp/keyman' && github.actor == 'keyman-server' && startsWith(github.head_ref, 'auto/') }}
steps:
- name: auto approve PR from keyman-server
shell: bash
@ -23,7 +23,6 @@ jobs:
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: |

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"
@ -30,11 +31,12 @@ env:
STATUS_CONTEXT: 'Ubuntu Packaging'
DEBFULLNAME: 'Keyman GHA packager'
DEBEMAIL: 'support@keyman.com'
_builder_timings: false
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 }}
@ -134,7 +136,7 @@ jobs:
strategy:
fail-fast: true
matrix:
dist: [jammy, noble, plucky]
dist: [jammy, noble, plucky, questing]
steps:
- name: Checkout
@ -160,7 +162,7 @@ jobs:
strategy:
fail-fast: true
matrix:
dist: [questing]
dist: [devel]
steps:
- name: Checkout
@ -184,28 +186,53 @@ jobs:
runs-on: ubuntu-latest
steps:
# - name: Download Artifacts
# uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
# with:
# path: artifacts
# merge-multiple: true
# - name: Install dependencies
# run: |
# 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"
# 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: Free disk space
run: |
echo "Ignored for now - until working solution is in place (#13777)"
# Free disk space by removing some large directories that we don't use (#15171)
# see https://dev.to/mathio/squeezing-disk-space-from-github-actions-runners-an-engineers-guide-3pjg
# Remove Android SDKs (frees 12 GB on Ubuntu 24.04)
sudo rm -rf /usr/local/lib/android
# Remove Haskell (GHC) (frees 6.4 GB)
sudo rm -rf /usr/local/.ghcup
# Remove CodeQL and other toolcaches (frees 5.8 GB)
sudo rm -rf /opt/hostedtoolcache
# Remove .NET SDKs (frees 4.0 GB)
sudo rm -rf /usr/share/dotnet
- name: Download Source Artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: keyman-srcpkg
path: artifacts
- name: Download Binary Artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
path: artifacts
pattern: keyman-binarypkgs-*
merge-multiple: true
- name: Install dependencies
run: |
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
@ -332,6 +359,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

153
.github/workflows/npm-publish.yml vendored Normal file
View file

@ -0,0 +1,153 @@
#
# Keyman is copyright (C) SIL Global. MIT License.
#
# Created by mcdurdin on 2025-10-27
#
# This workflow publishes all our npm packages using Trusted Publishers OIDC
# permissions. It is triggered by a repository_dispatch event with the types
# `npm-publish:*`, from the trigger-builds infrastructure. For test builds, this
# does a `npm pack` only.
#
# Inputs:
# buildSha: The SHA of the commit to build, e.g. of the branch or
# refs/pull/1234/head for PR
# user: The user that triggered the build or created the PR
# isTestBuild: false for Releases, otherwise true
name: "npm pack/publish"
run-name: "npm pack/publish - branch: ${{ github.event.client_payload.branch }} sha: ${{ github.event.client_payload.buildSha }}, user: @${{ github.event.client_payload.user }}, isTestBuild: ${{ github.event.client_payload.isTestBuild }}"
on:
repository_dispatch:
types: ['npm-publish:*']
permissions:
id-token: write # Required for OIDC
contents: read
statuses: write
env:
GH_TOKEN: ${{ github.token }}
STATUS_CONTEXT: 'npm pack/publish'
IS_TEST_BUILD: ${{ github.event.client_payload.isTestBuild }}
# This property causes node.inc.sh to skip invocation of nvm, as otherwise, we
# end up downgrading to node v20 as of Keyman 19. See #15040 and corresponding
# change in node.inc.sh; we should be able to remove this in Keyman 20.
KEYMAN_CI_SKIP_NVM: true
jobs:
npm_publish:
name: Publish or pack @keymanapp packages
if: github.repository == 'keymanapp/keyman' || github.event.client_payload.force
runs-on: ubuntu-24.04
steps:
- name: Set env var for pack vs publish
run: |
if [[ "$IS_TEST_BUILD" == true ]]; then
echo "NPM_ACTION=pack" >> $GITHUB_ENV
else
echo "NPM_ACTION=publish" >> $GITHUB_ENV
fi
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
with:
ref: '${{ github.event.client_payload.buildSha }}'
- name: Set pending status
id: set_status
if: github.event.client_payload.isTestBuild == 'true'
shell: bash
run: |
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
/repos/$GITHUB_REPOSITORY/statuses/${{ github.event.client_payload.buildSha }} \
-f state='pending' \
-f target_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" \
-f description="npm $NPM_ACTION started" \
-f context="$STATUS_CONTEXT"
- name: Report initial npm/node versions
run: |
echo "Initial npm/node versions"
npm -v
node -v
- uses: actions/setup-node@v6
with:
node-version: '24.10'
- name: Report current npm/node versions
run: |
echo "Current npm/node versions"
npm -v
node -v
- name: Configure build environment
run: |
./resources/build/ci/npm-publish.sh configure
- name: npm pack/publish
run: |
export KEYMAN_TIER=$(cat TIER.md)
export GHA_TEST_BUILD="${{ github.event.client_payload.isTestBuild }}"
export GHA_BRANCH="${{ github.event.client_payload.branch }}"
echo "KEYMAN_TIER=$KEYMAN_TIER" >> $GITHUB_ENV
echo "GHA_TEST_BUILD=${GHA_TEST_BUILD}" >> $GITHUB_ENV
echo "GHA_BRANCH=${GHA_BRANCH}" >> $GITHUB_ENV
echo "KEYMAN_TIER: $KEYMAN_TIER"
echo "GHA_TEST_BUILD: $GHA_TEST_BUILD"
echo "GHA_BRANCH: $GHA_BRANCH"
if [[ -f ./resources/build/ci/npm-publish.sh ]]; then
./resources/build/ci/npm-publish.sh $NPM_ACTION
else
echo WARNING: npm-publish.sh is not yet available
fi
set_status:
name: Set result status on PR builds
needs: [npm_publish]
runs-on: ubuntu-latest
if: ${{ always() }}
steps:
- name: Set env var for pack vs publish
run: |
if [[ "$IS_TEST_BUILD" == true ]]; then
echo "NPM_ACTION=pack" >> $GITHUB_ENV
else
echo "NPM_ACTION=publish" >> $GITHUB_ENV
fi
- name: Set success
if: needs.npm_publish.result == 'success'
run: |
echo "RESULT=success" >> $GITHUB_ENV
echo "MSG=npm $NPM_ACTION succeeded" >> $GITHUB_ENV
- name: Set cancelled
if: needs.npm_publish.result == 'cancelled'
run: |
echo "RESULT=error" >> $GITHUB_ENV
echo "MSG=npm $NPM_ACTION cancelled" >> $GITHUB_ENV
- name: Set failure
if: needs.npm_publish.result == 'failure'
run: |
echo "RESULT=failure" >> $GITHUB_ENV
echo "MSG=npm $NPM_ACTION failed" >> $GITHUB_ENV
- name: Set final status
if: github.event.client_payload.isTestBuild == 'true'
run: |
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
/repos/$GITHUB_REPOSITORY/statuses/${{ github.event.client_payload.buildSha }} \
-f state="$RESULT" \
-f target_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" \
-f description="$MSG" \
-f context="$STATUS_CONTEXT"

View file

@ -55,43 +55,63 @@ 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 == 'npm pack/publish') {
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 +122,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 +139,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 +161,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 +175,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 +199,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 +211,7 @@ jobs:
conclusion,
output: {
title: description,
summary: ''
summary
}
});
}
@ -204,5 +226,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]);

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,381 @@
# Keyman Version History
## 19.0.170 alpha 2025-12-03
* chore(deps-dev): bump js-yaml from 4.1.0 to 4.1.1 in /resources/build/pr-build-status (#15173)
* chore(deps-dev): bump glob from 10.4.5 to 10.5.0 in /resources/build/pr-build-status (#15184)
* chore(deps): bump js-yaml and @microsoft/api-documenter (#15185)
* chore(deps): bump glob from 10.4.5 to 10.5.0 in /developer/src/server/src/win32/trayicon/addon-src (#15186)
* docs(developer): add/update API docs (#15275)
## 19.0.169 alpha 2025-12-02
* maint(core): verify that keyman_core_ldml.h is in sync (#15256)
* chore(common): Update FirstVoices keyboard versions in keyboards.csv (#15261)
## 19.0.168 alpha 2025-12-01
* chore(android): add intent to broadcast font name (#15193)
* docs(android): Document Keyman API for keyboard font change (#15247)
* docs(ios): Use /cdn/dev assets for Engine guides (#15253)
* fix(developer): handle errors in .keyman-touch-layout file when loading (#15243)
* fix(android): address keyboard height vs touch height race after keyboard height preference change (also min max heights + docs) (#13663)
## 19.0.167 alpha 2025-11-28
* change(windows): add unit testing to keyman32 build (#15244)
* refactor(windows): merge syskbdnt.cpp and syskbdnt64.cpp into RightAltEmulationCheck.cpp (#15245)
* chore(windows): add wow64kbd support app for researching WOW64 reqs (#15246)
## 19.0.166 alpha 2025-11-26
* chore(android): Clarify PR title for #15136 (#15224)
* fix(windows): escape backslashes in path in debugging message (#15236)
## 19.0.165 alpha 2025-11-21
* chore(mac): add keyTrace log (#14972)
* fix(developer): avoid race in page.onload vs kmw.init in Server (#15087)
* fix(windows): reinstall low level keyboard hook if it gets removed (#15179)
## 19.0.164 alpha 2025-11-20
* feat(windows): update wix to 3.14.1 (#15194)
## 19.0.163 alpha 2025-11-17
* chore(linux): Update debian changelog (#15133)
* fix(windows): handle exceptions when walking stack in exception handler (#15150)
* fix(windows): disable OutputDebugString from DllMain (#15151)
* change(windows): include map2pdb generation for relevant Delphi projects (#14799)
* change(windows): use circular buffer for logging (#15163)
* fix(windows): handle forward slash for KEYMAN_ROOT env var (#15156)
* maint(mac): handle hdiutil detach failure correctly in make-km-dmg.sh (#15176)
## 19.0.162 alpha 2025-11-14
* chore(linux): use `devel` for next Ubuntu release (#15157)
* maint(resources): add build timing report to builder (#15114)
* maint(resources): add `builder_launch` function (#15129)
* fix(web): improve determination of default path (#15159)
* maint(linux): free disk space before running autopkgtests (#15172)
## 19.0.161 alpha 2025-11-13
* docs(android): Add documentation explaining Android keyboard safety warning dialog (#15136)
## 19.0.160 alpha 2025-11-12
* fix(android): Change Alert Dialog instead of Toast if package missing keyboard and lexical-model files (#15098)
* chore(linux): add support for Ubuntu 26.04 LTS Resolute (#15137)
## 19.0.159 alpha 2025-11-11
* maint(core): fix option defaults for Meson >= 1.1 (#15131)
## 19.0.158 alpha 2025-11-10
* fix(web): add unit tests for DeadkeyTracker and fix a few bugs (#15113)
## 19.0.157 alpha 2025-11-06
* chore(common): Consolidate platformtest.js keyboard to `/common/test/keyboards/` (#15034)
* chore(common): Consolidate test9469.js keyboard to `/common/test/keyboards/` (#15053)
## 19.0.156 alpha 2025-11-05
* chore(linux): Update debian changelog (#14965)
* maint(resources): Docker container changes permissions to match the file owner uid and gid (#14900)
* fix(developer): remove package version if FollowKeyboardVersion is set in Package Editor (#15088)
* maint(windows): new timestamp server for test signing (#15091)
## 19.0.155 alpha 2025-11-03
* maint(resources): auto merge on branch name rather than label (#15084)
* change(developer): remove kmlmc and kmlmp (#15083)
## 19.0.154 alpha 2025-10-31
* chore(developer): further debugging for assertion failure (#15056)
* chore(developer): add breadcrumbs to trace sporadic crashes on exit (#15058)
* fix(developer): handle exceptions loading .keyman-touch-layout files (#15060)
* chore(developer): fix always-false nullish coalescing (#15061)
* chore(windows): upgrade VC++ projects to v143 (VS2022) (#15062)
## 19.0.153 alpha 2025-10-30
* test(developer): add kmc-analyze tests for AnalyzeOskCharacterUse output formats (#15030)
## 19.0.152 alpha 2025-10-30
* maint(resources): include sourcemaps in npm publish (#15051)
## 19.0.151 alpha 2025-10-29
* maint(resources): add repository record for all published packages (#15049)
## 19.0.150 alpha 2025-10-29
* maint(resources): set build flags for npm-publish (#15047)
## 19.0.149 alpha 2025-10-29
* maint(resources): don't report status check on release builds for npm-publish (#15045)
## 19.0.148 alpha 2025-10-29
* chore(web,android): Consolidate chirality.js test keyboard to `/common/test/resources/keyboards/` (#14989)
* maint(resources): npm publish - build all packages first (#15042)
## 19.0.147 alpha 2025-10-28
* maint(resources): move npm package publishing to GitHub Actions (#15029)
* maint(resources): use actions/setup-node to upgrade npm (#15035)
* maint(resources): use a string for version, rather than number (#15036)
* maint(resources): set env variables in npm publish GHA (#15037)
* maint(mac): minor tweaks to mac setup, using build.sh (#15028)
* maint(resources): add build and test for npm packaging and add status check results (#15038)
* maint(resources): add write permission for statuses for npm-publish (#15039)
* maint(resources): skip nvm in npm-publish script (#15041)
## 19.0.146 alpha 2025-10-27
* fix(mac): ignore non-Unicode key caps when preparing OSK (#15008)
* fix(developer): prevent ANSI keyboards crashing debugger (#15022)
* test(developer): add coverage tests for warning messages in kmc-analyze/AnalyzeOskCharacterUse (#15021)
## 19.0.145 alpha 2025-10-24
* fix(developer): do not treat backslash as a string escape in syntax highlighting (#15001)
* fix(developer): handle missing `begin Unicode` in KMW compiler (#15002)
* test(developer): add test to kmc-analyze for Warn_PreviousMapFileCouldNotBeLoaded (#14038)
## 19.0.144 alpha 2025-10-23
* chore(deps-dev): bump playwright from 1.46.1 to 1.56.1 (#14984)
* chore(web): revert "chore(deps-dev): bump playwright from 1.46.1 to 1.56.1" (#15000)
## 19.0.143 alpha 2025-10-20
* fix(android): Set the main app background white (#14935)
## 19.0.142 alpha 2025-10-17
* chore(common): add .model_info schema (#14967)
* chore(web): update helpURL documentation and float ui link (#14960)
* fix(linux): use `ibus` command to restart ibus (#14971)
* chore(linux): replace deprecated `parse_version` (#14973)
## 19.0.141 alpha 2025-10-16
* refactor(linux): extract functions (#14937)
* refactor(linux): remove no-longer-needed Python 3.7 code (#14956)
* chore(linux): don't report errors to Sentry when running autopkgtests (#14938)
* fix(linux): fix installing keyboard in shared area (#14953)
* fix(linux): install schema during `keyman-config/build.sh install` (#14962)
* test(linux): add regression tests for cli tools to autopkgtests (#14950)
## 19.0.140 alpha 2025-10-15
* fix(mac): add Microsoft Word to non-compliant app list (#14940)
## 19.0.139 alpha 2025-10-13
* fix(windows): osk: handle simulated left Control event when AltGr pressed (#14909)
## 19.0.138 alpha 2025-10-11
* fix(android): Update Sample1 text field to resize (#14922)
## 19.0.137 alpha 2025-10-10
* chore(common): Update crowdin strings for Vietnamese (#14915)
* maint(resources): fix watch bug for common files, and add more logging for triggering builds (#14893)
* docs(developer): Update keyboard-editor.md to clarify when entire row will be deleted (#14919)
## 19.0.136 alpha 2025-10-09
* fix(android): Refactor bottom inset calculations for in-app and system keyboard (#14873)
* fix(linux): replace `dbus-x11` dependency (#14895)
* chore(ios): renew certificate (#14914)
## 19.0.135 alpha 2025-10-08
* fix(android): Convert longpress delay menu to ConstraintLayout (#14897)
## 19.0.134 alpha 2025-10-07
* maint(resources): nest unit tests under parent block (#14871)
## 19.0.133 alpha 2025-10-06
* chore(linux): Update debian changelog (#14856)
* maint(linux): fix integration tests in Docker (#14817)
## 19.0.132 alpha 2025-10-03
* fix(windows): remove cached files in idle state (#14683)
## 19.0.131 alpha 2025-10-02
* refactor(web): cleanup in test scripts (#14870)
## 19.0.130 alpha 2025-09-30
* maint(linux): allow to remote debug integration tests in Docker (#14818)
* maint(resources): running the container for core in privileged mode (#14867)
## 19.0.129 alpha 2025-09-29
* chore(linux): remove no longer needed lintian overrides (#14810)
## 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)
@ -663,6 +1039,64 @@
* refactor(windows): rename `TKeymanMutex.MutexOwned` to `TakeOwnership` and add `ReleaseOwnership` (#13168)
* chore: increment to alpha 19.0 (#13187)
## 18.0.245 stable 2025-12-03
* chore(windows): VS2022 patch for stable-18.0 (#15081)
* maint(resources): use VERSION_ENVIRONMENT var for v18 (#15068)
* chore(linux): Update debian changelog (#14964)
* fix(windows): add check a fix for registry datatypes (#15175)
* chore(linux): Update debian changelog (#15132)
* feat(windows): update wix to 3.14.1 (#15195)
* fix(windows): reinstall low level keyboard hook if it gets removed (#15219)
## 18.0.244 stable 2025-10-31
* fix(linux): use `ibus` command to restart ibus (#14975)
* fix(android): Set the main app background white (#14982)
* fix(developer): do not treat backslash as a string escape in syntax highlighting (#15005)
* fix(developer): handle missing `begin Unicode` in KMW compiler (#15006)
* fix(mac): ignore non-Unicode key caps when preparing OSK (#15009)
* maint(resources): move NPM package publishing to GitHub Actions (#15054)
* chore(developer): add breadcrumbs to trace sporadic crashes on exit (#15059)
* chore(developer): further debugging for assertion failure (#15057)
## 18.0.243 stable 2025-10-15
* chore(linux): Update debian changelog (#14855)
* fix(android): Convert longpress delay menu to ConstraintLayout (#14906)
* fix(linux): replace `dbus-x11` dependency (#14898)
* chore(ios): renew certificate (#14917)
* fix(android): Refactor bottom inset calculations for inapp and system keyboard (#14913)
* fix(mac): add Microsoft Word to non-compliant app list (#14951)
## 18.0.242 stable 2025-09-29
* maint(linux): remove Oracular, add Questing (#14781)
* maint(resources): use VERSION variable in increment-version.sh (#14779)
* chore(linux): Update debian changelog (#14782)
* fix(developer): remove incorrect offset for compiler error line (#14792)
* maint(linux): improve getting PR# in `upload-to-debian.sh` (#14785)
* chore(linux): remove no longer needed lintian overrides (#14811)
* fix(android): Apply bottom insets to system keyboard for Keyman, KMSample2, and FirstVoices (#14833)
* fix(windows): disable .msi shortcut advertisement (#14829)
* feat(windows): improve startup time by caching locale names (#14832)
## 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)
@ -674,6 +1108,7 @@
* 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)

View file

@ -1 +1 @@
19.0.107
19.0.171

View file

@ -46,7 +46,7 @@ MODEL_PACKAGE_ID="nrc.en.mtnt"
MODELS_TARGET="android/KMAPro/kMAPro/src/main/assets/${MODEL_PACKAGE_ID}.model.kmp"
builder_describe_outputs \
configure "/${MODELS_TARGET}" \
build /android/KMAPro/kMAPro/build/outputs/apk/$CONFIG/keyman-${KEYMAN_VERSION}.apk
build /android/KMAPro/kMAPro/build/outputs/apk/$CONFIG/keyman-${KEYMAN_VERSION_FOR_FILENAME}.apk
#### Build
@ -77,13 +77,15 @@ 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"
# Convert markdown to html for offline help
build_help_html android KMAPro/kMAPro/src/main/assets/info
echo "BUILD_FLAGS $BUILD_FLAGS"
./gradlew $DAEMON_FLAG clean $BUILD_FLAGS
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

View file

@ -35,6 +35,7 @@ import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.widget.FrameLayout;
import android.content.Intent;
import io.sentry.android.core.SentryAndroid;
import io.sentry.Sentry;
@ -124,8 +125,9 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
}
ViewGroup parent = (ViewGroup) inputView.getParent();
if (parent != null)
if (parent != null) {
parent.removeView(inputView);
}
return inputView;
}
@ -139,6 +141,22 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
KMManager.updateSelectionRange(KMManager.KeyboardType.KEYBOARD_TYPE_SYSTEM);
}
private void sendCurrentFontName() {
Keyboard keyboardInfo = KMManager.getCurrentKeyboardInfo(this);
if (keyboardInfo != null) {
String fontName = keyboardInfo.getFont();
Intent intent;
if (BuildConfig.DEBUG) {
intent = new Intent("com.tavultesoft.kmapro.debug.keyboard_changed");
} else {
intent = new Intent("com.tavultesoft.kmapro.keyboard_changed");
}
intent.putExtra("fontName", fontName);
sendBroadcast(intent);
}
}
/**
* This is the main point where we do our initialization of the input method
* to begin operating on an application. At this point we have been
@ -197,6 +215,10 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
if (KMManager.isNumericField(inputType)) {
KMManager.setNumericLayer(KeyboardType.KEYBOARD_TYPE_SYSTEM);
}
if (KMManager.isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM)) {
sendCurrentFontName();
}
}
@Override
@ -237,7 +259,7 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
KMManager.onConfigurationChanged(newConfig);
}
// We should extend the touchable region so that Keyman sub keys menu can receive touch events outside the keyboard frame
// Update the touchable region of the Keyman keyboard
Point size = KMManager.getWindowSize(getApplicationContext());
int inputViewHeight = 0;
@ -245,9 +267,10 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
inputViewHeight = inputView.getHeight();
}
int navigationHeight = KMManager.getNavigationBarHeight(this, KeyboardType.KEYBOARD_TYPE_SYSTEM);
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);
@ -263,7 +286,10 @@ 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();
sendCurrentFontName();
}
@Override

View file

@ -95,9 +95,10 @@ public class AdjustKeyboardHeightActivity extends BaseActivity {
currentHeight -= y;
// Apply lower and upper bounds on currentHeight
int defaultHeight = (int) context.getResources().getDimension(R.dimen.keyboard_height);
currentHeight = Math.max(defaultHeight/2, currentHeight);
currentHeight = Math.min(defaultHeight*2, currentHeight);
int minKeyboardHeight = KMManager.getKeyboardHeightMin(context);
int maxKeyboardHeight = KMManager.getKeyboardHeightMax(context);
currentHeight = Math.max(minKeyboardHeight, currentHeight);
currentHeight = Math.min(maxKeyboardHeight, currentHeight);
refreshSampleKeyboard(context);
break;

View file

@ -57,6 +57,10 @@ public class AdjustLongpressDelayActivity extends BaseActivity {
final Context context = this;
setContentView(R.layout.activity_adjust_longpress_delay);
setupEdgeToEdge(R.id.adjust_longpress_layout);
setupStatusBarColors(R.color.keyman_blue, android.R.color.white);
Toolbar toolbar = (Toolbar) findViewById(R.id.titlebar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();

View file

@ -619,22 +619,19 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
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 ***
// Status bar height only used for portrait orientation
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);
if (lastOrientation == Configuration.ORIENTATION_PORTRAIT) {
// *** TO DO: Try to check if status bar is visible, set statusBarHeight to 0 if it is not visible ***
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
statusBarHeight = getResources().getDimensionPixelSize(resourceId);
}
}
int navigationBarHeight = KMManager.getNavigationBarHeight(context, KeyboardType.KEYBOARD_TYPE_INAPP);
Point size = KMManager.getWindowSize(context);
int screenHeight = size.y;
Log.d(TAG, "Main resizeTextView bannerHeight: " + bannerHeight);
textView.setHeight(
screenHeight - statusBarHeight - actionBarHeight - bannerHeight - keyboardHeight - navigationBarHeight);
}

View file

@ -85,7 +85,8 @@ public class PackageActivity extends BaseActivity implements
if (pkgTarget.equals(PackageProcessor.PP_TARGET_LEXICAL_MODELS)) {
kmpProcessor = new LexicalModelPackageProcessor(resourceRoot);
} else if (!pkgTarget.equals(PackageProcessor.PP_TARGET_KEYBOARDS)) {
showErrorToast(getString(R.string.no_targets_to_install));
// Not using showErrorToast to avoid cluttering Sentry with noise
showErrorDialog(context, pkgId, getString(R.string.no_targets_to_install));
return;
}
tempPackagePath = kmpProcessor.unzipKMP(kmpFile);

View file

@ -1,8 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
<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/adjust_longpress_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" />
@ -13,7 +16,8 @@
android:gravity="center"
android:orientation="horizontal"
android:paddingBottom="25dp"
android:paddingTop="60dp" >
android:paddingTop="60dp"
app:layout_constraintTop_toBottomOf="@id/titlebar" >
<ImageButton
android:id="@+id/delayTimeDownButton"
@ -56,6 +60,6 @@
android:layout_marginStart="@dimen/activity_horizontal_margin"
android:text="@string/longpress_delay_time"
android:textSize="@dimen/longpress_delay_label_textsize"
/>
app:layout_constraintTop_toBottomOf="@id/linearLayout" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -8,7 +8,7 @@
android:fitsSystemWindows="true"
android:layout_above="@+id/KMKeyboard"
android:orientation="vertical"
android:background="@color/yellow_background"
android:background="@android:color/white"
tools:context=".MainActivity">
<include layout="@layout/titlebar"

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

@ -93,19 +93,17 @@ public class BaseActivity extends AppCompatActivity {
// Allocate insets for system bars and display cutout (notch)
Insets insets = windowInsets.getInsets(
WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams)view.getLayoutParams();
mlp.topMargin = insets.top;
mlp.bottomMargin = insets.bottom;
mlp.leftMargin = insets.left;
mlp.rightMargin = insets.right;
view.setLayoutParams(mlp);
KMManager.applyInsetsToKeyboard(insets.left, insets.right, insets.bottom);
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;
});
// Trigger an initial pass
ViewCompat.requestApplyInsets(anchor);
}
/**
@ -120,13 +118,15 @@ public class BaseActivity extends AppCompatActivity {
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
WindowInsetsControllerCompat windowInsetsController =
WindowCompat.getInsetsController(window, window.getDecorView());
windowInsetsController.setAppearanceLightStatusBars(true);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM &&
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
window.setStatusBarColor(getColor(statusBarColor));
window.setNavigationBarColor(getColor(navigationBarColor));
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));
}
}
}

View file

@ -453,10 +453,22 @@ final class KMKeyboard extends WebView {
public void onResume() {
DisplayMetrics dms = context.getResources().getDisplayMetrics();
int kbWidth = (int) (dms.widthPixels / dms.density);
// Get the correct keyboard height for current orientation
int kbHeight = KMManager.getKeyboardHeight(context);
// Check if there's a pending height update for this specific keyboard type and orientation
int currentOrientation = KMManager.getOrientation(context);
if (KMManager.getAndClearPendingHeightUpdate(this.keyboardType, currentOrientation)) {
// There was a pending height update - force apply it now
RelativeLayout.LayoutParams params = KMManager.getKeyboardLayoutParams();
this.setLayoutParams(params);
}
// Ensure window is loaded for javascript functions
loadJavascript(KMString.format(
"window.onload = function(){ setOskWidth(\"%d\");"+
"setOskHeight(\"0\"); };", kbWidth));
"setOskHeight(\"%d\"); };", kbWidth, kbHeight));
if (this.getShouldShowHelpBubble()) {
this.showHelpBubbleAfterDelay(2000);
}

View file

@ -42,6 +42,7 @@ import android.view.Display;
import android.view.Surface;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;
import android.view.inputmethod.EditorInfo;
@ -52,6 +53,10 @@ import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.Insets;
import androidx.core.view.DisplayCutoutCompat;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import io.sentry.Breadcrumb;
import io.sentry.Sentry;
@ -86,6 +91,8 @@ public final class KMManager {
private static final String TAG = "KMManager";
private static ResourcesUpdateTool updateTool;
private static int inappBottomInset; // Bottom inset for in-app keyboard
private static int sysBottomInset; // Bottom inset for system keyboard
// Keyboard types
public enum KeyboardType {
@ -301,6 +308,12 @@ public final class KMManager {
private static final String KMKey_KeyboardHeightPortrait = "keyboardHeightPortrait";
private static final String KMKey_KeyboardHeightLandscape = "keyboardHeightLandscape";
// Preference keys for tracking pending keyboard height updates per keyboard type and orientation
private static final String KMKey_PendingHeightUpdate_InappPortrait = "pendingHeightUpdate_inapp_portrait";
private static final String KMKey_PendingHeightUpdate_InappLandscape = "pendingHeightUpdate_inapp_landscape";
private static final String KMKey_PendingHeightUpdate_SystemPortrait = "pendingHeightUpdate_system_portrait";
private static final String KMKey_PendingHeightUpdate_SystemLandscape = "pendingHeightUpdate_system_landscape";
public static final String KMKey_LongpressDelay = "longpressDelay";
public static final String KMKey_CustomHelpLink = "CustomHelpLink";
@ -701,10 +714,6 @@ public final class KMManager {
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);
@ -713,24 +722,28 @@ public final class KMManager {
/**
* 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(int left, int right, int bottom) {
// System keyboard
if (SystemKeyboard != null) {
applyInsetsPaddingToKeyboardView(SystemKeyboard, left, right, bottom);
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);
}
// In-App Keyboard
if (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) {
inappBottomInset = bottom;
} else if (keyboard.keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
sysBottomInset = bottom;
}
if (parent != null) {
// Keep existing top padding, add bottom padding for the inset
@ -742,8 +755,7 @@ public final class KMManager {
);
// No need to change WebView margins; keep height as-is
parent.requestLayout();
}
else {
} else {
// Fallback: apply on the WebView itself
keyboard.setPadding(
left,
@ -883,28 +895,46 @@ public final class KMManager {
@SuppressLint("InflateParams")
public static View createInputView(InputMethodService inputMethodService) {
//final Context context = appContext;
IMService = inputMethodService;
Context appContext = IMService.getApplicationContext();
final FrameLayout mainLayout = new FrameLayout(appContext);
mainLayout.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
if (SystemKeyboard == null) {
installSystemKeyboardInsetsListener(mainLayout);
return mainLayout;
}
RelativeLayout keyboardLayout = new RelativeLayout(appContext);
keyboardLayout.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
ViewGroup parent = (ViewGroup) SystemKeyboard.getParent();
if (parent != null)
if (parent != null) {
parent.removeView(SystemKeyboard);
}
keyboardLayout.addView(SystemKeyboard);
mainLayout.addView(keyboardLayout);
//mainLayout.addView(overlayLayout);
installSystemKeyboardInsetsListener(mainLayout);
return mainLayout;
}
private static void installSystemKeyboardInsetsListener(View anchor) {
ViewCompat.setOnApplyWindowInsetsListener(anchor, (v, insets) -> {
// System bars (status bar and navigation bar)
Insets systemBarInsets = insets.getInsets(
WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
sysBottomInset = systemBarInsets.bottom;
applyInsetsToKeyboard(KeyboardType.KEYBOARD_TYPE_SYSTEM, systemBarInsets.left, systemBarInsets.right, sysBottomInset);
return insets;
});
// Trigger an initial pass
ViewCompat.requestApplyInsets(anchor);
}
public static void onStartInput(EditorInfo attribute, boolean restarting) {
if (!restarting && SystemKeyboard != null) {
String packageName = attribute.packageName;
@ -2391,12 +2421,11 @@ public final class KMManager {
}
height = defaultHeightForContext;
} else {
// Applying gating to 50%-200% of default height (following Keyman)
if (height < (defaultHeightForContext / 2)) {
height = (int) (defaultHeightForContext / 2);
} else if (height > (defaultHeightForContext * 2)) {
height = (int) (defaultHeightForContext * 2);
}
// Ensure keyboard height stay within min/max bounds
int minKeyboardHeight = KMManager.getKeyboardHeightMin(context, orientation);
int maxKeyboardHeight = KMManager.getKeyboardHeightMax(context, orientation);
height = Math.max(minKeyboardHeight, height);
height = Math.min(maxKeyboardHeight, height);
// Store the new height based on the current orientation
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
@ -2413,16 +2442,22 @@ public final class KMManager {
KeyboardHeight_Context_Portrait_Current = height;
}
editor.commit();
// Confirm new LayoutParams for in-app or system keyboards
// If keyboard is not currently loaded, set a pending flag so height gets applied when keyboard loads
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP)) {
InAppKeyboard.loadJavascript(KMString.format("setOskHeight('%s')", height));
RelativeLayout.LayoutParams params = getKeyboardLayoutParams();
InAppKeyboard.setLayoutParams(params);
} else {
setPendingHeightUpdate(KeyboardType.KEYBOARD_TYPE_INAPP, orientation);
}
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM)) {
SystemKeyboard.loadJavascript(KMString.format("setOskHeight('%s')", height));
RelativeLayout.LayoutParams params = getKeyboardLayoutParams();
SystemKeyboard.setLayoutParams(params);
} else {
setPendingHeightUpdate(KeyboardType.KEYBOARD_TYPE_SYSTEM, orientation);
}
}
@ -2473,21 +2508,179 @@ public final class KMManager {
KeyboardHeight_Context_Landscape_Default = (int) newResources.getDimension(R.dimen.keyboard_height);
}
// Get the navigation bar height (if visible) in pixels
private 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;
/**
* Get the navigation bar height (if visible) in pixels
* @param context - The context
* @param {KeyboardType} - KeyboardType.KEYBOARD_TYPE_INAPP or KeyboardType.KEYBOARD_TYPE_SYSTEM
* @return int - navigation bar height in pixels
*/
public static int getNavigationBarHeight(Context context, KeyboardType keyboardType) {
// bottomInset already updated from the inset listener
if (keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP) {
return inappBottomInset;
} else if (keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
return sysBottomInset;
}
// Navigation bar visible so get the height
resourceId = context.getResources().getIdentifier(
// Navigation bar height uninitialized, so log and revert to legacy computation.
// Note. Inconsistent for gesture vs 3-button navigation mode #14893
KMLog.LogError(TAG, "bottom inset not initialized for keyboard type: " + keyboardType);
int resourceId = context.getResources().getIdentifier(
"navigation_bar_height", "dimen", "android");
return (resourceId > 0) ? context.getResources().getDimensionPixelSize(resourceId) : 0;
}
/**
* Returns the maximum allowed height of the keyboard frame for the current device orientation.
* The maximum height is calculated as twice the default keyboard height for the orientation.
* Use this method to get the upper bound for keyboard height adjustments.
*
* @param context The context.
* @return The maximum allowed keyboard height in density-independent pixels (dp),
* or {@link #KeyboardHeight_Reset} (0) if orientation is invalid.
* @see #getKeyboardHeightMax(Context, int)
* @see #getKeyboardHeightMin(Context)
* @see #applyKeyboardHeight(Context, int)
*/
public static int getKeyboardHeightMax(Context context) {
int orientation = getOrientation(context);
return getKeyboardHeightMax(context, orientation);
}
/**
* Returns the maximum allowed height of the keyboard frame for the specified orientation.
* The maximum height is calculated as twice the default keyboard height for the orientation.
* Use this method to get the upper bound for keyboard height adjustments.
*
* @param context The context.
* @param orientation The screen orientation: {@link Configuration#ORIENTATION_PORTRAIT}
* or {@link Configuration#ORIENTATION_LANDSCAPE}.
* @return The maximum allowed keyboard height in density-independent pixels (dp),
* or {@link #KeyboardHeight_Reset} (0) if orientation is invalid.
* @see #getKeyboardHeightMin(Context, int)
* @see #applyKeyboardHeight(Context, int, int)
*/
public static int getKeyboardHeightMax(Context context, int orientation) {
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
return (int) KMManager.KeyboardHeight_Context_Landscape_Default * 2;
} else /* orientation == Configuration.ORIENTATION_PORTRAIT */ {
return (int) KMManager.KeyboardHeight_Context_Portrait_Default * 2;
}
}
/**
* Returns the minimum allowed height of the keyboard frame for the current device orientation.
* The minimum height is calculated as half the default keyboard height for the orientation.
* Use this method to get the lower bound for keyboard height adjustments.
*
* @param context The context.
* @return The minimum allowed keyboard height in density-independent pixels (dp),
* or {@link #KeyboardHeight_Reset} (0) if orientation is invalid.
* @see #getKeyboardHeightMin(Context, int)
* @see #getKeyboardHeightMax(Context)
* @see #applyKeyboardHeight(Context, int)
*/
public static int getKeyboardHeightMin(Context context) {
int orientation = getOrientation(context);
return getKeyboardHeightMin(context, orientation);
}
/**
* Returns the minimum allowed height of the keyboard frame for the specified orientation.
* The minimum height is calculated as half the default keyboard height for the orientation.
* Use this method to get the lower bound for keyboard height adjustments.
*
* @param context The context.
* @param orientation The screen orientation: {@link Configuration#ORIENTATION_PORTRAIT}
* or {@link Configuration#ORIENTATION_LANDSCAPE}.
* @return The minimum allowed keyboard height in density-independent pixels (dp),
* or {@link #KeyboardHeight_Reset} (0) if orientation is invalid.
* @see #getKeyboardHeightMax(Context, int)
* @see #applyKeyboardHeight(Context, int, int)
*/
public static int getKeyboardHeightMin(Context context, int orientation) {
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
return (int) KMManager.KeyboardHeight_Context_Landscape_Default / 2;
} else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
return (int) KMManager.KeyboardHeight_Context_Portrait_Default / 2;
} else {
return KMManager.KeyboardHeight_Reset;
}
}
/**
* Returns the preference key for tracking pending keyboard height updates.
* This allows separate tracking for each keyboard type (in-app/system) and orientation.
*
* @param keyboardType The keyboard type: {@link KeyboardType#KEYBOARD_TYPE_INAPP}
* or {@link KeyboardType#KEYBOARD_TYPE_SYSTEM}.
* @param orientation The screen orientation: {@link Configuration#ORIENTATION_PORTRAIT}
* or {@link Configuration#ORIENTATION_LANDSCAPE}.
* @return The preference key string, or null if parameters are invalid.
*/
private static String getPendingHeightKey(KeyboardType keyboardType, int orientation) {
if (keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP) {
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
return KMKey_PendingHeightUpdate_InappPortrait;
} else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
return KMKey_PendingHeightUpdate_InappLandscape;
}
} else if (keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
return KMKey_PendingHeightUpdate_SystemPortrait;
} else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
return KMKey_PendingHeightUpdate_SystemLandscape;
}
}
return null;
}
/**
* Sets a flag indicating that a keyboard height update is pending for the specified
* keyboard type and orientation. This is used when the keyboard is not currently loaded,
* so the height update will be applied when the keyboard loads.
*
* @param keyboardType The keyboard type: {@link KeyboardType#KEYBOARD_TYPE_INAPP}
* or {@link KeyboardType#KEYBOARD_TYPE_SYSTEM}.
* @param orientation The screen orientation: {@link Configuration#ORIENTATION_PORTRAIT}
* or {@link Configuration#ORIENTATION_LANDSCAPE}.
*/
private static void setPendingHeightUpdate(KeyboardType keyboardType, int orientation) {
String pendingKey = getPendingHeightKey(keyboardType, orientation);
if (pendingKey == null) {
return;
}
SharedPreferences prefs = appContext.getSharedPreferences(KMManager.KMEngine_PrefsKey, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(pendingKey, true);
editor.commit();
}
/**
* Checks if there is a pending keyboard height update for the specified keyboard type
* and orientation, and clears the flag if present.
*
* @param keyboardType The keyboard type: {@link KeyboardType#KEYBOARD_TYPE_INAPP}
* or {@link KeyboardType#KEYBOARD_TYPE_SYSTEM}.
* @param orientation The screen orientation: {@link Configuration#ORIENTATION_PORTRAIT}
* or {@link Configuration#ORIENTATION_LANDSCAPE}.
* @return true if there was a pending height update (which has been cleared), false otherwise.
*/
protected static boolean getAndClearPendingHeightUpdate(KeyboardType keyboardType, int orientation) {
String pendingKey = getPendingHeightKey(keyboardType, orientation);
if (pendingKey == null) {
return false;
}
SharedPreferences prefs = appContext.getSharedPreferences(KMManager.KMEngine_PrefsKey, Context.MODE_PRIVATE);
boolean hasPending = prefs.getBoolean(pendingKey, false);
if (hasPending) {
SharedPreferences.Editor editor = prefs.edit();
editor.remove(pendingKey);
editor.commit();
}
return hasPending;
}
/**
* Get the size of the area the window would occupy.
* API 30+

View file

@ -9,6 +9,7 @@
android:orientation="vertical">
<include layout="@layout/list_toolbar"
android:id="@+id/list_appbar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />

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,45 +1,43 @@
<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.constraintlayout.widget.ConstraintLayout
<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">
<androidx.appcompat.widget.Toolbar
android:id="@+id/list_toolbar"
android:layout_width="match_parent"
<TextView
android:id="@+id/bar_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent">
android:layout_gravity="center_horizontal"
android:gravity="center"
android:ellipsize="end"
android:singleLine="true"
android:text="@string/title_add_keyboard"
android:textSize="@dimen/titlebar_label_textsize"
android:textStyle="bold" />
<TextView
android:id="@+id/bar_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:ellipsize="end"
android:singleLine="true"
android:text="@string/title_add_keyboard"
android:textSize="@dimen/titlebar_label_textsize"
android:textStyle="bold" />
</androidx.appcompat.widget.Toolbar>
</androidx.appcompat.widget.Toolbar>
<!-- optional accent -->
<View
android:id="@+id/bar_accent"
style="@style/AppTheme.Divider"
android:layout_width="match_parent"
android:layout_height="2dp"
app:layout_constraintTop_toBottomOf="@id/list_toolbar"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<!-- optional accent -->
<View
android:id="@+id/bar_accent"
style="@style/AppTheme.Divider"
android:layout_width="match_parent"
android:layout_height="2dp"
app:layout_constraintTop_toBottomOf="@id/list_toolbar"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.appbar.AppBarLayout>

View file

@ -80,6 +80,14 @@
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard">Lỗi bàn phím nghiêm trọng với %1$s: %2$s cho ngôn ngữ %3$s. Đang tải bàn phím mặc định.</string>
<!-- Context: Fatal keyboard error. -->
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language).">Lỗi trong bàn phím %1$s:%2$s cho ngôn ngữ %3$s.</string>
<!-- Context: Alert Dialog body: System WebView not installed -->
<string name="body_install_webview" comment="Notification when WebView not installed"> Keyman yêu cầu phải cài đặt WebView\".</string>
<!-- Context: Alert Dialog body: System WebView installed but not enabled -->
<string name="body_enable_webview" comment="Notification when WebView installed but not enabled"> Keyman yêu cầu phải bật WebView\".</string>
<!-- Context Button label: Install WebView -->
<string name="label_install_webview" comment="Button label to install webview">Cài đặt WebView</string>
<!-- Context Button label: Enable WebView -->
<string name="label_enable_webview" comment="Button label to enable webview">Bật WebView</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Kiểm tra từ điển liên quan để tải về</string>
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download">Không thể kết nối với server của Keyman để kiểm tra từ điển được liên kết để tải xuống</string>
@ -113,6 +121,8 @@
<string name="failed_to_retrieve_file" comment="Failed to retrieve downloaded file">Không thể thu hồi file đã tải về</string>
<!-- Context: General Updates -->
<string name="update_check_unavailable" comment="Error message when a Keyman server can't be reached">Không thể kết nối với máy chủ!</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 đâ bị vô hiệu hoá - không thể kiểm tra các bản cập nhật</string>
<!-- Context: General Updates -->
<string name="update_check_current" comment="Notification that all resources are up to date">"Toàn bộ kho dữ liệu đã được cập nhật!"</string>
<!-- Context: General Updates -->

View file

@ -2,7 +2,9 @@ package com.keyman.kmsample1;
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;
@ -25,6 +27,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
public static Context context;
private ConstraintLayout constraintLayout;
private KMTextView textView;
private int lastOrientation = Configuration.ORIENTATION_UNDEFINED;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -99,6 +102,18 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
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);
}
@ -116,6 +131,10 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
KMManager.onConfigurationChanged(newConfig);
resizeTextView(textView.isKeyboardVisible());
lastOrientation = newConfig.orientation;
}
@Override
@ -131,11 +150,39 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
@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);
}
int navigationBarHeight = KMManager.getNavigationBarHeight(context, KeyboardType.KEYBOARD_TYPE_INAPP);
Point size = KMManager.getWindowSize(context);
int screenHeight = size.y;
textView.setHeight(
screenHeight - statusBarHeight - actionBarHeight - bannerHeight - keyboardHeight - navigationBarHeight);
}
@Override

View file

@ -1,3 +1,4 @@
<?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"
@ -5,16 +6,16 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:layout_above="@+id/KMKeyboard"
android:orientation="vertical"
tools:context=".MainActivity">
<com.keyman.engine.KMTextView
android:id="@+id/kmTextView"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_alignParentTop="true"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textMultiLine"
android:inputType="textMultiLine|textNoSuggestions"
android:gravity="top"
android:scrollbars="vertical"
app:layout_constraintTop_toTopOf="parent"

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, KeyboardType.KEYBOARD_TYPE_SYSTEM);
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);

View file

@ -1,56 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<KeymanDeveloperProject>
<Options>
<Version>2.0</Version>
<BuildPath>$PROJECTPATH</BuildPath>
<SourcePath>$PROJECTPATH\</SourcePath>
<CompilerWarningsAsErrors>True</CompilerWarningsAsErrors>
<WarnDeprecatedCode>True</WarnDeprecatedCode>
<CheckFilenameConventions>True</CheckFilenameConventions>
<SkipMetadataFiles>True</SkipMetadataFiles>
<ProjectType>lexicalmodel</ProjectType>
</Options>
<Files>
<File>
<ID>id_165fd1598b940efe4a973aeb6eb61aba</ID>
<Filename>keyboardharness.kps</Filename>
<Filepath>keyboardharness.kps</Filepath>
<FileVersion>1.0</FileVersion>
<FileType>.kps</FileType>
<Details>
<Name>keyboardharness</Name>
<Copyright>©2020 SIL International</Copyright>
<Version>1.0</Version>
</Details>
</File>
<File>
<ID>id_f9b1213d4e83cb32ce1ead1ee852d618</ID>
<Filename>chirality.js</Filename>
<Filepath>..\..\..\..\..\..\..\web\testing\chirality\chirality.js</Filepath>
<FileVersion></FileVersion>
<FileType>.js</FileType>
<ParentFileID>id_165fd1598b940efe4a973aeb6eb61aba</ParentFileID>
</File>
<File>
<ID>id_e01841691fa129428aea555142873f69</ID>
<Filename>platformtest.js</Filename>
<Filepath>..\..\..\..\..\..\..\web\testing\platform\platformtest.js</Filepath>
<FileVersion></FileVersion>
<FileType>.js</FileType>
<ParentFileID>id_165fd1598b940efe4a973aeb6eb61aba</ParentFileID>
</File>
<File>
<ID>id_03df89bb84a1aa6cab40dfebf3a04c5b</ID>
<Filename>code2001.ttf</Filename>
<Filepath>code2001.ttf</Filepath>
<FileVersion></FileVersion>
<FileType>.ttf</FileType>
<ParentFileID>id_165fd1598b940efe4a973aeb6eb61aba</ParentFileID>
</File>
<File>
<ID>id_2d07bd2b8c94e9600ac1a920c8cb9e0a</ID>
<Filename>longpress.js</Filename>
<Filepath>longpress.js</Filepath>
<FileVersion></FileVersion>
<FileType>.js</FileType>
<ParentFileID>id_165fd1598b940efe4a973aeb6eb61aba</ParentFileID>
</File>
</Files>
</KeymanDeveloperProject>

View file

@ -20,17 +20,23 @@
</Info>
<Files>
<File>
<Name>..\..\..\..\..\..\..\web\src\test\manual\web\chirality\chirality.js</Name>
<Name>../../../../../../../common/test/resources/keyboards/chirality.js</Name>
<Description>File chirality.js</Description>
<CopyLocation>0</CopyLocation>
<FileType>.js</FileType>
</File>
<File>
<Name>..\..\..\..\..\..\..\web\src\test\manual\web\platform\platformtest.js</Name>
<Name>../../../../../../../common/test/keyboards/platform-rules/platformtest.js</Name>
<Description>File platformtest.js</Description>
<CopyLocation>0</CopyLocation>
<FileType>.js</FileType>
</File>
<File>
<Name>../../../../../../../common/test/keyboards/test9469/build/test9469.js</Name>
<Description>File test9469.js</Description>
<CopyLocation>0</CopyLocation>
<FileType>.js</FileType>
</File>
<File>
<Name>code2001.ttf</Name>
<Description>Font Code2001</Description>
@ -61,6 +67,14 @@
<Language ID="en">English</Language>
</Languages>
</Keyboard>
<Keyboard>
<Name>Test9469</Name>
<ID>test9469</ID>
<Version>1.0</Version>
<Languages>
<Language ID="en">English</Language>
</Languages>
</Keyboard>
<Keyboard>
<Name>longpress '"\|5% +</Name>
<ID>longpress</ID>

View file

@ -89,6 +89,21 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
KMManager.KMDefault_KeyboardFont);
KMManager.addKeyboard(this, platformtestKBbInfo);
// Issue #9469 Verify special characters in keyamnweb-osk.ttf keyboard
Keyboard specialKBInfo = new Keyboard(
"keyboardharness",
"test9469",
"test9469 Keyboard",
"en",
"English",
"1.0",
"",
"",
true,
KMManager.KMDefault_KeyboardFont,
KMManager.KMDefault_KeyboardFont);
KMManager.addKeyboard(this, specialKBInfo);
// Final K_ENTER test keyboard
Keyboard finalKBInfo = new Keyboard(
"final",
@ -103,21 +118,6 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
KMManager.KMDefault_KeyboardFont,
KMManager.KMDefault_KeyboardFont);
KMManager.addKeyboard(this, finalKBInfo);
// Issue #9469 Verify special characters in keyamnweb-osk.ttf keyboard
Keyboard specialKBInfo = new Keyboard(
"test9469",
"test9469",
"test9469 Keyboard",
"en",
"English",
"1.0",
"",
"",
true,
KMManager.KMDefault_KeyboardFont,
KMManager.KMDefault_KeyboardFont);
KMManager.addKeyboard(this, specialKBInfo);
}
@Override

View file

@ -62,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}"
@ -87,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
#

View file

@ -0,0 +1,23 @@
---
title: Keyman for Android APIs
---
## Summary
The **`Keyman for Android`** app has the following APIs for integration:
## Description
The Keyman for Android app installs as an Input Method Extension. When selected as the active system keyboard, it provides the following APIs:
## Intents
When the Keyman system keyboard changes the associated font name, the following Intent is broadcast so the integrating application can match the font:
```java
intent = new Intent("com.tavultesoft.kmapro.keyboard_changed");
intent.putExtra("fontName", fontName);
sendBroadcast(intent);
```

View file

@ -23,7 +23,7 @@ KMManager.applyKeyboardHeight(Context context, int height, int orientation)
: The context
`height`
: The height of the keyboard frame in *density-independent pixels (dp)*. Pass `KMManager.KeyboardHeight_Reset` to reset the keyboard height (for current orientation) to device-specific defaults.
: The height of the keyboard frame in *density-independent pixels (dp)*. Pass `KMManager.KeyboardHeight_Reset` to reset the keyboard height (for the current orientation) to device-specific defaults.
`orientation` _(Optional)_
: Accepts a [screen orientation](https://developer.android.com/training/multiscreen/screensizes#TaskUseOriQuali) value. This is most useful if you want to change the size of the keyboard in the other (non-current) orientation. If `orientation` is not defined, keyboard height is set for the current device orientation.
@ -78,3 +78,5 @@ The following script illustrates the use of `applyKeyboardHeight()` to reset key
## See also
* [getKeyboardHeight()](getKeyboardHeight)
* [getKeyboardHeightMax()](getKeyboardHeightMax)
* [getKeyboardHeightMin()](getKeyboardHeightMin)

View file

@ -55,3 +55,5 @@ or
## See also
- [applyKeyboardHeight()](applyKeyboardHeight)
- [getKeyboardHeightMax](getKeyboardHeightMax)
- [getKeyboardHeightMin](getKeyboardHeightMin)

View file

@ -0,0 +1,57 @@
---
title: KMManager.getKeyboardHeightMax()
---
## Summary
The **`getKeyboardHeightMax()`** method returns the maximum allowed height of the keyboard frame for this context.
## Syntax
``` javascript
KMManager.getKeyboardHeightMax(Context context)
```
or
``` javascript
KMManager.getKeyboardHeightMax(Context context, int orientation)
```
### Parameters
`context`
: The context.
`orientation` _(Optional)_
: Accepts a [screen orientation](https://developer.android.com/training/multiscreen/screensizes#TaskUseOriQuali) value. This is most useful if you want to get the size of the keyboard in the other (non-current) orientation. If `orientation` is not defined, maximum keyboard height is returned for the current device orientation.
### Returns
Returns the maximum allowed height of the keyboard frame in *density-independent pixels
(dp)*.
## Description
Use this method to get the maximum allowed height of the keyboard frame.
## Examples
### Example: Using `getKeyboardHeightMax()`
The following script illustrate the use of `getKeyboardHeightMax()`:
``` javascript
int maxKeyboardHeight = KMManager.getKeyboardHeightMax(this);
```
or
```java
import android.content.res.Configuration;
...
// Get the maximum allowed Keyman keyboard height for landscape mode.
int maxKeyboardHeightLandscape = KMManager.getKeyboardHeightMax(this, Configuration.ORIENTATION_LANDSCAPE);
```
## See also
- [applyKeyboardHeight()](applyKeyboardHeight)
- [getKeyboardHeight()](getKeyboardHeight)
- [getKeyboardHeightMin()](getKeyboardHeightMin)

View file

@ -0,0 +1,57 @@
---
title: KMManager.getKeyboardHeightMin()
---
## Summary
The **`getKeyboardHeightMin()`** method returns the minimum allowed height of the keyboard frame for this context.
## Syntax
``` javascript
KMManager.getKeyboardHeightMin(Context context)
```
or
``` javascript
KMManager.getKeyboardHeightMin(Context context, int orientation)
```
### Parameters
`context`
: The context.
`orientation` _(Optional)_
: Accepts a [screen orientation](https://developer.android.com/training/multiscreen/screensizes#TaskUseOriQuali) value. This is most useful if you want to get the size of the keyboard in the other (non-current) orientation. If `orientation` is not defined, minimum keyboard height is returned for the current device orientation.
### Returns
Returns the minimum allowed height of the keyboard frame in *density-independent pixels
(dp)*.
## Description
Use this method to get the minimum allowed height of the keyboard frame.
## Examples
### Example: Using `getKeyboardHeightMin()`
The following script illustrate the use of `getKeyboardHeightMin()`:
``` javascript
int minKeyboardHeight = KMManager.getKeyboardHeightMin(this);
```
or
```java
import android.content.res.Configuration;
...
// Get the minimum allowed Keyman keyboard height for landscape mode.
int minKeyboardHeightLandscape = KMManager.getKeyboardHeightMin(this, Configuration.ORIENTATION_LANDSCAPE);
```
## See also
- [applyKeyboardHeight()](applyKeyboardHeight)
- [getKeyboardHeight()](getKeyboardHeight)
- [getKeyboardHeightMax()](getKeyboardHeightMax)

View file

@ -0,0 +1,49 @@
---
title: KMManager.getNavigationBarHeight()
---
## Summary
The **`getNavigationBarHeight()`** method returns the height of the navigation bar corresponding to the in-app or system keyboard.
## Syntax
``` javascript
KMManager.getNavigationBarHeight(Context context, KeyboardType keyboardType)
```
### Parameters
`context`
: The context.
`keyboardType`
: The keyboard type. `KEYBOARD_TYPE_INAPP` or
`KEYBOARD_TYPE_SYSTEM`.)
### Returns
Returns the height of the navigation bar in pixels.
## Description
Use this method to get the height of the navigation bar, which depends on
the keyboard type (in-app or system) and navigation mode (gesture, 2-button, or 3-button navigation).
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, KeyboardType.SYSTEM_KEYBOARD);
outInsets.contentTopInsets = inputViewHeight - bannerHeight - kbHeight - navigationHeight;
```
## See Also
- [`getBannerHeight()`](getBannerHeight)
- [`getKeyboardHeight()`](getKeyboardHeight)

View file

@ -101,6 +101,12 @@ The KMManager is the core class which provides most of the methods and constants
[`getKeyboardHeight()`](getKeyboardHeight)
: returns the height of the keyboard frame
[`getKeyboardHeightMax()`](getKeyboardHeightMax)
: returns the maximum allowed height of the keyboard frame
[`getKeyboardHeightMin()`](getKeyboardHeightMin)
: returns the minimum allowed height of the keyboard frame
[`getKeyboardIndex()`](getKeyboardIndex)
: returns index number of the specified keyboard in keyboards list
@ -149,6 +155,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)

View file

@ -27,8 +27,12 @@ see [What's New](whatsnew) for breaking changes</a>
[KeyboardEventHandler](KeyboardEventHandler/)
: Provides keyboard and lexical-model events and methods to notify registered listeners
[Keyman for Android app](KMAPro/)
: Public APIs for integrating with the Keyman application
## See also
* [Keyman Engine for Android 18.0](/developer/engine/android/18.0/)
* [Keyman Engine for Android 17.0](/developer/engine/android/17.0/)
* [Keyman Engine for Android 16.0](/developer/engine/android/16.0/)
* [Keyman Engine for Android 15.0](/developer/engine/android/15.0/)

View file

@ -2,5 +2,7 @@
title: What's New in Keyman Engine 19.0 for Android
---
* Added API for broadcasting when Keyman system keyboard font changes (#15193)
## See Also
* [Keyman Engine for Android Documentation](index)

View file

@ -0,0 +1,28 @@
---
title: Does Keyman include adware or spyware?
---
No!
We never have and never will include adware or spyware components.
When [enabling Keyman as a system keyboard](../start/enabling-system-keyboard), Android displays the following warning, which is the same message that is displayed for any keyboard app that is installed onto an Android device.
![](../android_images/attention.png)
> Attention: This input method may be able to collect all the text you
> type, including personal data like passwords and credit card numbers.
> It comes from the app Keyman. Use this input method? Cancel / OK
The Keyman app does not record the keystrokes you type and does not send keystrokes off-device, unlike many other keyboard apps. All predictive text suggestions are entirely on your device.
Keyman is an open source application, and you are most welcome to inspect the source code at https://github.com/keymanapp/keyman.
## Checking for Updates
Keyman does contact the keyman.com website periodically to check for updates for the keyboards and predictive text dictionaries you have installed.
In this process, the only information communicated is the current version of Keyman and the version of the keyboards and dictionaries.
## Crash Report Information
When enabled, the Keyman app will send crash reporting information to [Sentry](https://keyman.sentry.io). No personally identifiable information or keyboard strokes are recorded. This can be disabled in the Keyman [settings menu](../basic/config/).

View file

@ -5,5 +5,6 @@ title: About Keyman for Android
* [Welcome to Keyman](welcome)
* [What's New](whatsnew)
* [System Requirements](system-requirements)
* [Does Keyman include adware or spyware?](adware)
* [Data Privacy Policy](privacy)
* [Version History](history)

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -6,6 +6,7 @@ title: Keyman for Android 19.0 Help
* [Welcome to Keyman](about/welcome)
* [What's New](about/whatsnew)
* [System Requirements](about/system-requirements)
* [Does Keyman include adware or spyware?](about/adware)
* [Data Privacy Policy](about/privacy)
* [Version History](about/history)

View file

@ -0,0 +1,19 @@
# model_info
* **model_info.schema.json**
Documentation at https://help.keyman.com/developer/cloud/model_info
* Primary version:
* https://github.com/keymanapp/api.keyman.com/tree/master/schemas/model_info
* Synchronized copies at:
* https://github.com/keymanapp/keyman/tree/master/common/schemas/model_info
# .model_info version history
## 2023-08-11 2.0 stable
* Removed:
`.links`
`.related[].note`
- Source .model_info files are no longer needed, so source vs distribution
model_info distinction is removed

View file

@ -0,0 +1,49 @@
{
"$schema": "http://json-schema.org/schema#",
"$ref": "#/definitions/ModelInfo",
"definitions": {
"ModelInfo": {
"type": "object",
"properties": {
"id": { "type": "string" },
"name": { "type": "string" },
"authorName": { "type": "string" },
"authorEmail": { "type": "string", "format": "email" },
"description": { "type": "string" },
"license": { "type": "string", "enum": ["mit"] },
"languages": { "type": "array", "items": { "type": "string" }, "uniqueItems": true },
"lastModifiedDate": { "type": "string", "format": "date-time" },
"packageFilename": { "type": "string" },
"packageFileSize": { "type": "number" },
"jsFilename": { "type": "string" },
"jsFileSize": { "type": "number" },
"isRTL": { "type": "boolean" },
"packageIncludes": { "type": "array", "items": { "type": "string", "enum": ["fonts"] }, "additionalItems": false },
"version": { "type": "string" },
"minKeymanVersion": { "type": "string", "pattern": "^\\d+\\.0$" },
"helpLink": { "type": "string", "pattern": "^http(s)?://help\\.keyman(-staging)?\\.com(.localhost)?/model/" },
"sourcePath": { "type": "string", "pattern": "^(release|experimental)/.+/.+$" },
"related": { "type": "object", "patternProperties": {
".": { "$ref": "#/definitions/ModelRelatedInfo" }
},
"additionalProperties": false
}
},
"required": [
"license", "languages"
],
"additionalProperties": false
},
"ModelRelatedInfo": {
"type": "object",
"properties": {
"deprecates": { "type": "boolean" },
"deprecatedBy": { "type": "boolean" }
},
"required": [],
"additionalProperties": false
}
}
}

View file

@ -0,0 +1,499 @@
if (typeof keyman === 'undefined') {
console.log('Keyboard requires KeymanWeb 10.0 or later');
if (typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
} else {
KeymanWeb.KR(new Keyboard_platformtest());
}
function Keyboard_platformtest() {
this._v = (typeof keyman != "undefined" && typeof keyman.version == "string") ? parseInt(keyman.version, 10) : 9;
this.KI = "Keyboard_platformtest";
this.KN = "PlatformTest";
this.KMINVER = "10.0";
this.KV = null;
this.KDU = 0;
this.KH = '';
this.KM = 0;
this.KBVER = "1.0";
this.KMBM = 0x0;
this.s20 = "touch";
this.s21 = "hardware";
this.s24 = "windows";
this.s25 = "android";
this.s26 = "ios";
this.s27 = "macosx";
this.s28 = "linux";
this.s31 = "desktop";
this.s32 = "tablet";
this.s33 = "phone";
this.s36 = "native";
this.s37 = "web";
this.s40 = "ie";
this.s41 = "chrome";
this.s42 = "edge";
this.s43 = "firefox";
this.s44 = "safari";
this.s45 = "opera";
this.s48 = "touch";
this.s49 = "hardware";
this.s52 = "platform-x";
this.s55 = "WinDOWS";
this.s56 = "ANDroid";
this.s57 = "iOS";
this.s58 = "macOSX";
this.s59 = "LINUX";
this.s61 = "touch";
this.s62 = "hardware";
this.s63 = "windows";
this.s64 = "android";
this.s65 = "ios";
this.s66 = "macosx";
this.s67 = "linux";
this.s68 = "desktop";
this.s69 = "tablet";
this.s70 = "phone";
this.s71 = "native";
this.s72 = "web";
this.s73 = "ie";
this.s74 = "chrome";
this.s75 = "edge";
this.s76 = "firefox";
this.s77 = "safari";
this.s78 = "opera";
this.s79 = "touch";
this.s80 = "hardware";
this.s81 = "platform-x";
this.s82 = "WinDOWS";
this.s83 = "ANDroid";
this.s84 = "iOS";
this.s85 = "macOSX";
this.s86 = "LINUX";
this.KVER = "18.0.241.0";
this.KVS = [];
this.gs = function(t, e) {
return this.g0(t, e);
};
this.gs = function(t, e) {
return this.g0(t, e);
};
this.g0 = function(t, e) {
var k = KeymanWeb,
r = 0,
m = 0;
if (k.KKM(e, 16384, 65)) {
if (1) {
r = m = 1;
k.KDC(0, t);
r = this.g1(t, e);
m = 2;
}
}
if (!m && k.KIK(e)) {
r = 1;
k.KDC(-1, t);
r = this.g9(t, e);
m = 2;
}
return r;
};
this.g1 = function(t, e) {
var k = KeymanWeb,
r = 1,
m = 0;
if (k.KIFS(31, this.s20, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, "touch");
} else if (k.KIFS(31, this.s21, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, "hardware");
}
if (m == 1) {
k.KDC(-1, t);
r = this.g2(t, e);
m = 2;
}
if (!m) {
k.KDC(-1, t);
k.KO(-1, t, "undefined");
r = this.g2(t, e);
m = 2;
}
return r;
};
this.g2 = function(t, e) {
var k = KeymanWeb,
r = 1,
m = 0;
if (k.KIFS(31, this.s24, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " windows");
} else if (k.KIFS(31, this.s25, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " android");
} else if (k.KIFS(31, this.s26, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " ios");
} else if (k.KIFS(31, this.s27, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " macosx");
} else if (k.KIFS(31, this.s28, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " linux");
}
if (m == 1) {
k.KDC(-1, t);
r = this.g3(t, e);
m = 2;
}
if (!m) {
k.KDC(-1, t);
k.KO(-1, t, " undefined");
r = this.g3(t, e);
m = 2;
}
return r;
};
this.g3 = function(t, e) {
var k = KeymanWeb,
r = 1,
m = 0;
if (k.KIFS(31, this.s31, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " desktop");
} else if (k.KIFS(31, this.s32, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " tablet");
} else if (k.KIFS(31, this.s33, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " phone");
}
if (m == 1) {
k.KDC(-1, t);
r = this.g4(t, e);
m = 2;
}
if (!m) {
k.KDC(-1, t);
k.KO(-1, t, " undefined");
r = this.g4(t, e);
m = 2;
}
return r;
};
this.g4 = function(t, e) {
var k = KeymanWeb,
r = 1,
m = 0;
if (k.KIFS(31, this.s36, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " native");
} else if (k.KIFS(31, this.s37, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " web");
}
if (m == 1) {
k.KDC(-1, t);
r = this.g5(t, e);
m = 2;
}
if (!m) {
k.KDC(-1, t);
k.KO(-1, t, " undefined");
r = this.g5(t, e);
m = 2;
}
return r;
};
this.g5 = function(t, e) {
var k = KeymanWeb,
r = 1,
m = 0;
if (k.KIFS(31, this.s40, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " ie");
} else if (k.KIFS(31, this.s41, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " chrome");
} else if (k.KIFS(31, this.s42, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " edge");
} else if (k.KIFS(31, this.s43, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " firefox");
} else if (k.KIFS(31, this.s44, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " safari");
} else if (k.KIFS(31, this.s45, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " opera");
}
if (m == 1) {
k.KDC(-1, t);
r = this.g6(t, e);
m = 2;
}
if (!m) {
k.KDC(-1, t);
k.KO(-1, t, " undefined");
r = this.g6(t, e);
m = 2;
}
return r;
};
this.g6 = function(t, e) {
var k = KeymanWeb,
r = 1,
m = 0;
if (!k.KIFS(31, this.s48, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " !touch");
} else if (!k.KIFS(31, this.s49, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " !hardware");
}
if (m == 1) {
k.KDC(-1, t);
r = this.g7(t, e);
m = 2;
}
if (!m) {
k.KDC(-1, t);
k.KO(-1, t, " undefined");
r = this.g7(t, e);
m = 2;
}
return r;
};
this.g7 = function(t, e) {
var k = KeymanWeb,
r = 1,
m = 0;
if (k.KIFS(31, this.s52, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " platform-x");
}
if (m == 1) {
k.KDC(-1, t);
r = this.g8(t, e);
m = 2;
}
if (!m) {
k.KDC(-1, t);
k.KO(-1, t, " undefined");
r = this.g8(t, e);
m = 2;
}
return r;
};
this.g8 = function(t, e) {
var k = KeymanWeb,
r = 1,
m = 0;
if (k.KIFS(31, this.s55, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " windows");
} else if (k.KIFS(31, this.s56, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " android");
} else if (k.KIFS(31, this.s57, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " ios");
} else if (k.KIFS(31, this.s58, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " macosx");
} else if (k.KIFS(31, this.s59, t)) {
m = 1;
k.KDC(0, t);
k.KO(-1, t, " linux");
}
if (!m) {
k.KDC(-1, t);
k.KO(-1, t, " undefined");
}
return r;
};
this.g9 = function(t, e) {
var k = KeymanWeb,
r = 0,
m = 0;
if (k.KKM(e, 16384, 69)) {
if (k.KIFS(31, this.s82, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " Windows");
} else if (k.KIFS(31, this.s83, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " Android");
} else if (k.KIFS(31, this.s84, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " iOS");
} else if (k.KIFS(31, this.s85, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " macOS");
} else if (k.KIFS(31, this.s86, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " Linux");
} else if (1) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " [OS Undefined (case insensitive test)]");
}
} else if (k.KKM(e, 16384, 73)) {
if (k.KIFS(31, this.s68, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " Desktop");
} else if (k.KIFS(31, this.s69, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " Tablet");
} else if (k.KIFS(31, this.s70, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " Phone");
} else if (1) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " [FF Undefined]");
}
} else if (k.KKM(e, 16384, 79)) {
if (k.KIFS(31, this.s63, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " Windows");
} else if (k.KIFS(31, this.s64, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " Android");
} else if (k.KIFS(31, this.s65, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " iOS");
} else if (k.KIFS(31, this.s66, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " OSX");
} else if (k.KIFS(31, this.s67, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " Linux");
} else if (1) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " [OS Undefined]");
}
} else if (k.KKM(e, 16384, 80)) {
if (k.KIFS(31, this.s61, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, "Touch");
} else if (k.KIFS(31, this.s62, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, "Hardware");
} else if (1) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, "[UI Undefined]");
}
} else if (k.KKM(e, 16384, 82)) {
if (k.KIFS(31, this.s81, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " Platform-X");
} else if (1) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " [Platform-X Undefined]");
}
} else if (k.KKM(e, 16384, 84)) {
if (!k.KIFS(31, this.s79, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " !Touch");
} else if (!k.KIFS(31, this.s80, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " !Hardware");
} else if (1) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " [Inverted OS Undefined]");
}
} else if (k.KKM(e, 16384, 85)) {
if (k.KIFS(31, this.s71, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " Native");
} else if (k.KIFS(31, this.s72, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " Web");
} else if (1) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " [Nativeness Undefined]");
}
} else if (k.KKM(e, 16384, 89)) {
if (k.KIFS(31, this.s73, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " IE");
} else if (k.KIFS(31, this.s74, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " Chrome");
} else if (k.KIFS(31, this.s75, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " Edge");
} else if (k.KIFS(31, this.s76, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " Firefox");
} else if (k.KIFS(31, this.s77, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " Safari");
} else if (k.KIFS(31, this.s78, t)) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " Opera");
} else if (1) {
r = m = 1;
k.KDC(0, t);
k.KO(-1, t, " [Browser Undefined]");
}
}
return r;
};
}

Binary file not shown.

Binary file not shown.

View file

@ -4,22 +4,25 @@
<BuildPath></BuildPath>
<CompilerWarningsAsErrors>False</CompilerWarningsAsErrors>
<WarnDeprecatedCode>True</WarnDeprecatedCode>
<CheckFilenameConventions>True</CheckFilenameConventions>
<ProjectType>keyboard</ProjectType>
</Options>
<Files>
<File>
<ID>id_c1593d698cb624690005787147089dab</ID>
<Filename>platformtest.kmn</Filename>
<Filepath>platformtest.kmn</Filepath>
<Filepath>source/platformtest.kmn</Filepath>
<FileVersion>1.0</FileVersion>
<FileType>.kmn</FileType>
<Details>
<Name>PlatformTest</Name>
<Copyright>© SIL Global</Copyright>
</Details>
</File>
<File>
<ID>id_18e336f9ad890bf821ff56e1a7764478</ID>
<Filename>platformtest.kps</Filename>
<Filepath>platformtest.kps</Filepath>
<Filepath>source/platformtest.kps</Filepath>
<FileVersion></FileVersion>
<FileType>.kps</FileType>
<Details>
@ -28,8 +31,8 @@
</File>
<File>
<ID>id_61f04814d427ce02953436f3ea7d7a65</ID>
<Filename>PlatformTest.kmx</Filename>
<Filepath>PlatformTest.kmx</Filepath>
<Filename>platformtest.kmx</Filename>
<Filepath>platformtest.kmx</Filepath>
<FileVersion></FileVersion>
<FileType>.kmx</FileType>
<ParentFileID>id_18e336f9ad890bf821ff56e1a7764478</ParentFileID>

View file

@ -1,5 +1,8 @@
store(&TARGETS) 'any'
store(&VERSION) '10.0'
store(&TARGETS) 'any'
store(&NAME) 'PlatformTest'
store(&KEYBOARDVERSION) '1.0'
store(&COPYRIGHT) '© SIL Global'
begin Unicode > use(main)
group(main) using keys

View file

@ -17,16 +17,17 @@
<Info>
<Version URL=""></Version>
<Name URL="">PlatformTest</Name>
<Description URL="">Test keyboard for displaying platform information.</Description>
</Info>
<Files>
<File>
<Name>PlatformTest.kmx</Name>
<Description>Keyboard PlatformTest</Description>
<Name>../platformtest.kmx</Name>
<Description>Keyboard Platformtest</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kmx</FileType>
</File>
<File>
<Name>platformtest.js</Name>
<Name>../platformtest.js</Name>
<Description>File platformtest.js</Description>
<CopyLocation>0</CopyLocation>
<FileType>.js</FileType>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -1,6 +1,6 @@
c test9469 generated from template at 2023-08-17 19:34:17
c with name "test9469"
store(&VERSION) '10.0'
store(&VERSION) '14.0'
store(&NAME) 'test9469'
store(&COPYRIGHT) '© 2023 SIL International'
store(&KEYBOARDVERSION) '1.1'

View file

@ -20,6 +20,7 @@
<Copyright URL="">© 2023 SIL International</Copyright>
<Author URL="">SIL International</Author>
<Version URL=""></Version>
<Description URL="">Test keyboard for displaying special characters rendered with keymanweb-osk.ttf</Description>
</Info>
<Files>
<File>

View file

@ -3,7 +3,8 @@
/**
* Please note that this is an experimental handwritten keyboard designed for initial testing of modifier chirality support for KeymanWeb.
* As it is handcoded, it uses multiple codesize optimization techniques that Keyman Developer simply does not and will not employ, and it
* will only work with KeymanWeb based implementations. (Not supported for Keyman Desktop or Keyman for Mac.)
* will only work with KeymanWeb based implementations. (Not supported for Keyman for Windows,
* Keyman for Linux, or Keyman for macOS.)
*
* (This keyboard was written before Developer's implementation of chirality support and was used to bootstrap KeymanWeb chirality, serving
* as the initial wave of testing.)

View file

@ -0,0 +1,8 @@
# Custom mocha reporter for TeamCity
This is an enhanced version of the [mocha-teamcity-reporter](https://github.com/travisjeffery/mocha-teamcity-reporter)
(renamed to *.cjs) that allows to specify the parent flowId so that the tests
can be grouped together under the parent block in TeamCity.
If [travisjeffery/mocha-teamcity-reporter#69](https://github.com/travisjeffery/mocha-teamcity-reporter/issues/69)
gets fixed one day, we can go back to using the original version.

View file

@ -0,0 +1,259 @@
/* eslint-disable complexity */
/**
* Teamcity doc reference https://confluence.jetbrains.com/display/TCD10/Build+Script+Interaction+with+TeamCity
*
* Module dependencies.
*/
'use strict';
const processPID = process.pid.toString();
const TEST_IGNORED = `##teamcity[testIgnored name='%s' message='%s']`;
const SUITE_START = `##teamcity[testSuiteStarted name='%s']`;
const SUITE_END = `##teamcity[testSuiteFinished name='%s' duration='%s']`;
const SUITE_END_NO_DURATION = `##teamcity[testSuiteFinished name='%s']`;
const TEST_START = `##teamcity[testStarted name='%s' captureStandardOutput='true']`;
const TEST_FAILED = `##teamcity[testFailed name='%s' message='%s' details='%s' captureStandardOutput='true']`;
const TEST_FAILED_COMPARISON = `##teamcity[testFailed type='comparisonFailure' name='%s' message='%s' \
details='%s' captureStandardOutput='true' actual='%s' expected='%s']`;
const TEST_END = `##teamcity[testFinished name='%s' duration='%s']`;
const TEST_END_NO_DURATION = `##teamcity[testFinished name='%s']`;
const FLOW_START = `##teamcity[flowStarted flowId='%s' parent='%s']`;
const FLOW_END = `##teamcity[flowFinished flowId='%s']`;
const Mocha = require('mocha');
const {
EVENT_SUITE_BEGIN,
EVENT_TEST_BEGIN,
EVENT_TEST_FAIL,
EVENT_TEST_PENDING,
EVENT_TEST_END,
EVENT_HOOK_BEGIN,
EVENT_HOOK_END,
EVENT_SUITE_END,
EVENT_RUN_END
} = Mocha.Runner.constants;
const util = require('util');
let Base, log, logError;
Base = require('mocha').reporters.Base;
log = console.log;
logError = console.error;
let flowIds = [];
/**
* Escape the given `str`.
*/
function escape(str) {
if (!str) return '';
return str
.toString()
.replace(/\x1B.*?m/g, '') // eslint-disable-line no-control-regex
.replace(/\|/g, '||')
.replace(/\n/g, '|n')
.replace(/\r/g, '|r')
.replace(/\[/g, '|[')
.replace(/\]/g, '|]')
.replace(/\u0085/g, '|x')
.replace(/\u2028/g, '|l')
.replace(/\u2029/g, '|p')
.replace(/'/g, '|\'');
}
function isNil(value) {
return value == null; // eslint-disable-line
}
function formatString() {
let formattedArguments = [];
const args = Array.prototype.slice.call(arguments, 0);
// Format all arguments for TC display (it escapes using the pipe char).
let tcMessage = args.shift();
args.forEach((param) => {
formattedArguments.push(escape(param));
});
formattedArguments.unshift(tcMessage);
return util.format.apply(util, formattedArguments);
}
function handleFlow(isStarted, nestFlows) {
if (!nestFlows) {
return;
}
if (isStarted) {
flowIds.push(Math.floor(Math.random() * 100000 + 1));
log(formatString(FLOW_START, flowIds[flowIds.length - 1], flowIds[flowIds.length - 2]));
} else {
log(formatString(FLOW_END, flowIds[flowIds.length - 1]));
flowIds.pop();
}
}
function getFlowId() {
return flowIds[flowIds.length - 1];
}
/**
* Initialize a new `Teamcity` reporter.
*
* @param {Runner} runner
* @param {options} options
* @api public
*/
function Teamcity(runner, options) {
options = options || {};
const reporterOptions = options.reporterOptions || {};
let flowIdOpt, useStdError, recordHookFailures, actualVsExpected, ignoreHookWithName, displayIgnoredAsIgnored;
(reporterOptions.flowId) ? flowIdOpt = reporterOptions.flowId : flowIdOpt = process.env['MOCHA_TEAMCITY_FLOWID'] || processPID;
(reporterOptions.useStdError) ? useStdError = reporterOptions.useStdError : useStdError = process.env['USE_STD_ERROR'];
(reporterOptions.recordHookFailures) ? recordHookFailures = reporterOptions.recordHookFailures : recordHookFailures = process.env['RECORD_HOOK_FAILURES'];
(reporterOptions.actualVsExpected) ? actualVsExpected = reporterOptions.actualVsExpected : actualVsExpected = process.env['ACTUAL_VS_EXPECTED'];
(reporterOptions.ignoreHookWithName) ? ignoreHookWithName = reporterOptions.ignoreHookWithName : ignoreHookWithName = process.env['IGNORE_HOOK_WITH_NAME'];
(reporterOptions.displayIgnoredAsIgnored)
? displayIgnoredAsIgnored = reporterOptions.displayIgnoredAsIgnored
: displayIgnoredAsIgnored = process.env['DISPLAY_IGNORED_AS_IGNORED'];
(useStdError) ? useStdError = (useStdError.toLowerCase() === 'true') : useStdError = false;
(recordHookFailures) ? recordHookFailures = (recordHookFailures.toLowerCase() === 'true') : recordHookFailures = false;
(displayIgnoredAsIgnored) ? displayIgnoredAsIgnored = (displayIgnoredAsIgnored.toLowerCase() === 'true') : displayIgnoredAsIgnored = false;
(ignoreHookWithName) ? ignoreHookWithName : null;
actualVsExpected ? actualVsExpected = (actualVsExpected.toLowerCase() === 'true') : actualVsExpected = false;
Base.call(this, runner);
let stats = this.stats;
const topLevelSuite = reporterOptions.topLevelSuite || process.env['MOCHA_TEAMCITY_TOP_LEVEL_SUITE'];
const parentFlowId = reporterOptions.parentFlowId || process.env['MOCHA_TEAMCITY_PARENT_FLOW_ID'];
if (parentFlowId) {
flowIds.push(parentFlowId);
} else {
flowIds.push(flowIdOpt);
}
const hasParentFlowId = !!parentFlowId;
const ignoredTests = {};
const testState = { pending: 0 };
runner.on(EVENT_SUITE_BEGIN, function (suite) {
handleFlow(true, hasParentFlowId);
if (suite.root) {
if (topLevelSuite) {
log(formatString(SUITE_START, topLevelSuite));
}
return;
}
suite.startDate = new Date();
log(formatString(SUITE_START, suite.title));
});
runner.on(EVENT_TEST_BEGIN, function (test) {
if (displayIgnoredAsIgnored && ignoredTests[`${test.title}-${getFlowId()}`] === testState.pending) {
return;
}
handleFlow(true, hasParentFlowId);
log(formatString(TEST_START, test.title));
});
runner.on(EVENT_TEST_FAIL, function (test, err) {
let isHook = false;
if (test.title.includes(`"before all" hook`) ||
test.title.includes(`"before each" hook`) ||
test.title.includes(`"after all" hook`) ||
test.title.includes(`"after each" hook`)
) {
isHook = true;
}
if(actualVsExpected && (err.actual && err.expected)){
if (useStdError) {
logError(formatString(TEST_FAILED_COMPARISON,
test.title, err.message, err.stack, err.actual, err.expected));
} else {
log(formatString(TEST_FAILED_COMPARISON, test.title, err.message, err.stack, err.actual, err.expected));
}
} else{
if (useStdError) {
logError(formatString(TEST_FAILED, test.title, err.message, err.stack));
} else {
log(formatString(TEST_FAILED, test.title, err.message, err.stack));
}
}
// Log testFinished for failed hook (hook end event is not fired for failed hook)
if (recordHookFailures && !ignoreHookWithName || recordHookFailures && ignoreHookWithName && !test.title.includes(ignoreHookWithName)) {
if (isHook) {
if(isNil(test.duration)){
log(formatString(TEST_END_NO_DURATION, test.title));
} else {
log(formatString(TEST_END, test.title, test.duration.toString()));
}
handleFlow(false, hasParentFlowId);
}
}
});
runner.on(EVENT_TEST_PENDING, function (test) {
log(formatString(TEST_IGNORED, test.title, test.title));
if (displayIgnoredAsIgnored) {
ignoredTests[`${test.title}-${getFlowId()}`] = testState.pending;
} else {
handleFlow(true, hasParentFlowId)
}
});
runner.on(EVENT_TEST_END, function (test) {
if (displayIgnoredAsIgnored && ignoredTests[`${test.title}-${getFlowId()}`] === testState.pending) {
delete ignoredTests[`${test.title}-${getFlowId()}`];
return;
}
if(isNil(test.duration)){
log(formatString(TEST_END_NO_DURATION, test.title));
} else {
log(formatString(TEST_END, test.title, test.duration.toString()));
}
handleFlow(false, hasParentFlowId);
});
runner.on(EVENT_HOOK_BEGIN, function (test) {
if (recordHookFailures && !ignoreHookWithName || recordHookFailures && ignoreHookWithName && !test.title.includes(ignoreHookWithName)) {
handleFlow(true, hasParentFlowId);
log(formatString(TEST_START, test.title));
}
});
runner.on(EVENT_HOOK_END, function (test) {
if (recordHookFailures && !ignoreHookWithName || recordHookFailures && ignoreHookWithName && !test.title.includes(ignoreHookWithName)) {
if(isNil(test.duration)){
log(formatString(TEST_END_NO_DURATION, test.title));
} else {
log(formatString(TEST_END, test.title, test.duration.toString()));
}
handleFlow(false, hasParentFlowId);
}
});
runner.on(EVENT_SUITE_END, function (suite) {
if (!suite.root) {
log(formatString(SUITE_END, suite.title, new Date() - suite.startDate));
}
handleFlow(false, hasParentFlowId);
});
runner.on(EVENT_RUN_END, function () {
let duration;
(typeof stats === 'undefined') ? duration = null : duration = stats.duration;
if (topLevelSuite) {
isNil(duration)
? log(formatString(SUITE_END_NO_DURATION, topLevelSuite))
: log(formatString(SUITE_END, topLevelSuite, duration));
handleFlow(false, hasParentFlowId);
}
});
}
/**
* Expose `Teamcity`.
*/
exports = module.exports = Teamcity;

View file

@ -18,7 +18,6 @@
"homepage": "https://github.com/keymanapp/keyman#readme",
"devDependencies": {
"@keymanapp/resources-gosh": "*",
"mocha-teamcity-reporter": "^4.0.0",
"typescript": "^5.4.5"
},
"dependencies": {

View file

@ -7,7 +7,6 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "$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 ################################
@ -15,10 +14,7 @@ builder_describe "Build the include script for current Keyman version" \
configure \
clean \
build \
test \
"publish publish to npm" \
"--npm-publish+ For publish, do a npm publish, not npm pack (only for CI)" \
"--dry-run,-n don't actually publish, just dry run"
test
builder_describe_outputs \
configure "/node_modules" \
@ -56,4 +52,3 @@ export default KEYMAN_VERSION;
builder_run_action clean rm -rf version.inc.ts keyman-version.mts build/
builder_run_action configure node_select_version_and_npm_ci
builder_run_action build do_build
builder_run_action publish ci_publish_npm

View file

@ -1,5 +1,10 @@
{
"name": "@keymanapp/keyman-version",
"repository": {
"type": "git",
"url": "git+https://github.com/keymanapp/keyman.git",
"directory": "common/web/keyman-version"
},
"description": "Keyman global version data",
"exports": {
".": {

View file

@ -7,16 +7,12 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "$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" \
"configure" \
"build" \
"test" \
"publish publish to npm" \
"--npm-publish+ For publish, do a npm publish, not npm pack (only for CI)" \
"--dry-run,-n don't actually publish, just dry run"
"test"
builder_describe_outputs \
configure /common/web/langtags/src/imports/langtags.js \
@ -43,4 +39,3 @@ 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 ci_publish_npm

View file

@ -33,7 +33,8 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/keymanapp/keyman.git"
"url": "git+https://github.com/keymanapp/keyman.git",
"directory": "common/web/langtags"
},
"sideEffects": false
}

View file

@ -7,7 +7,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "$KEYMAN_ROOT/resources/build/utils.inc.sh"
. "$KEYMAN_ROOT/resources/build/node.inc.sh"
. "$KEYMAN_ROOT/resources/build/ci/ci-publish.inc.sh"
. "$KEYMAN_ROOT/resources/build/typescript.inc.sh"
builder_describe "Build Keyman common file types module" \
"@/core/include/ldml" \
@ -15,10 +15,7 @@ builder_describe "Build Keyman common file types module" \
"configure" \
"build" \
"clean" \
"test" \
"publish publish to npm" \
"--npm-publish+ For publish, do a npm publish, not npm pack (only for CI)" \
"--dry-run,-n don't actually publish, just dry run"
"test"
builder_describe_outputs \
configure /common/web/types/src/schemas/kpj.schema.ts \
@ -81,29 +78,9 @@ function do_configure() {
node_select_version_and_npm_ci
}
function do_test() {
local MOCHA_FLAGS=
if builder_is_running_on_teamcity; then
# we're running in TeamCity
MOCHA_FLAGS="-reporter mocha-teamcity-reporter"
fi
eslint .
tsc --build tests
readonly C8_THRESHOLD=60
# Excludes are defined in package.json
c8 -skip-full --reporter=lcov --reporter=text --lines $C8_THRESHOLD --statements $C8_THRESHOLD --branches $C8_THRESHOLD --functions $C8_THRESHOLD mocha ${MOCHA_FLAGS} "${builder_extra_params[@]}"
builder_echo warning "Coverage thresholds are currently $C8_THRESHOLD%, which is lower than ideal."
builder_echo warning "Please increase threshold in build.sh as test coverage improves."
}
#-------------------------------------------------------------------------------------------------------------------
builder_run_action clean rm -rf ./build/ ./tsconfig.tsbuildinfo ./src/schemas/ ./node_modules/ ./obj/
builder_run_action configure do_configure
builder_run_action build tsc --build
builder_run_action test do_test
builder_run_action publish ci_publish_npm
builder_run_action test typescript_run_eslint_mocha_tests 60

View file

@ -54,7 +54,8 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/keymanapp/keyman.git"
"url": "git+https://github.com/keymanapp/keyman.git",
"directory": "common/web/types"
},
"c8": {
"all": true,
@ -74,6 +75,7 @@
"src/ldml-keyboard/unicodeset-parser-api.ts",
"src/keyman-touch-layout/keyman-touch-layout-file-writer.ts",
"src/osk/osk.ts",
"src/util/types.ts",
"src/schemas/*",
"tests/",
"src/keyboard-object.ts",

View file

@ -10,6 +10,8 @@ uses
Winapi.Windows,
jwaimagehlp,
jwantstatus,
jwawinbase,
sentry;
@ -229,20 +231,41 @@ var
Skip: Integer;
LastMask: WORD;
begin
// Floating point state may be broken here, so let's mask it out and continue
// We'll restore state afterwards
LastMask := Get8087CW;
Set8087CW_Threadsafe($1332);
try
if (ExceptionInfo.ExceptionRecord.ExceptionCode <> EXCEPTION_ACCESS_VIOLATION) and
(ExceptionInfo.ExceptionRecord.ExceptionCode <> EXCEPTION_ILLEGAL_INSTRUCTION) and
(ExceptionInfo.ExceptionRecord.ExceptionCode <> EXCEPTION_PRIV_INSTRUCTION) and
(ExceptionInfo.ExceptionRecord.ExceptionCode <> EXCEPTION_POSSIBLE_DEADLOCK) and
(ExceptionInfo.ExceptionRecord.ExceptionCode <> EXCEPTION_IN_PAGE_ERROR) and
(ExceptionInfo.ExceptionRecord.ExceptionCode <> EXCEPTION_STACK_OVERFLOW) and
(ExceptionInfo.ExceptionRecord.ExceptionCode <> STATUS_INVALID_PARAMETER) and
(ExceptionInfo.ExceptionRecord.ExceptionCode <> cDelphiException) then
begin
// These are the most common exceptions we wish to catch; we won't get
// call stacks for any other exception types, but we will still log
// important exceptions
Exit(0);
end;
if ExceptionInfo.ExceptionRecord.ExceptionCode = cDelphiException
then Skip := DELPHI_FRAMES_TO_SKIP
else Skip := 0;
CaptureStackTraceForException(ExceptionInfo.ExceptionRecord.ExceptionAddress, ExceptionInfo, Skip);
// Floating point state may be broken here, so let's mask it out and continue
// We'll restore state afterwards
LastMask := Get8087CW;
Set8087CW_Threadsafe($1332);
// Restore FP state
Set8087CW_Threadsafe(LastMask);
if ExceptionInfo.ExceptionRecord.ExceptionCode = cDelphiException
then Skip := DELPHI_FRAMES_TO_SKIP
else Skip := 0;
CaptureStackTraceForException(ExceptionInfo.ExceptionRecord.ExceptionAddress, ExceptionInfo, Skip);
Result := 0; //EXCEPTION_CONTINUE_SEARCH;
// Restore FP state
Set8087CW_Threadsafe(LastMask);
Result := 0; //EXCEPTION_CONTINUE_SEARCH;
except
// Silently handle errors here because we can't do anything with them;
// we'll just get no stack
Result := 0;
end;
end;
constructor TSentryClient.Create(AOptions: TSentryClientOptions; const ALogger: string; AFlags: TSentryClientFlags);

View file

@ -1,119 +1,119 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{40BFECE3-7FC0-4649-A3DC-E017FF84C75B}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<Optimization>Disabled</Optimization>
</ClCompile>
<Link>
<TargetMachine>MachineX86</TargetMachine>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<TargetMachine>MachineX86</TargetMachine>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="fileutils.cpp" />
<ClCompile Include="log.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="tds2dbg.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="fileutils.h" />
<ClInclude Include="log.h" />
<ClInclude Include="tds2dbg.h" />
<ClInclude Include="tdscvstructs.h" />
</ItemGroup>
<ItemGroup>
<None Include="tds2dbg.bin" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{40BFECE3-7FC0-4649-A3DC-E017FF84C75B}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<Optimization>Disabled</Optimization>
</ClCompile>
<Link>
<TargetMachine>MachineX86</TargetMachine>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<TargetMachine>MachineX86</TargetMachine>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="fileutils.cpp" />
<ClCompile Include="log.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="tds2dbg.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="fileutils.h" />
<ClInclude Include="log.h" />
<ClInclude Include="tds2dbg.h" />
<ClInclude Include="tdscvstructs.h" />
</ItemGroup>
<ItemGroup>
<None Include="tds2dbg.bin" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -395,7 +395,7 @@ var
begin
// On developer machines, if we are running within the source repo, then use
// those paths
keyman_root := GetEnvironmentVariable('KEYMAN_ROOT');
keyman_root := GetEnvironmentVariable('KEYMAN_ROOT').Replace('/', '\');
if (keyman_root <> '') and SameText(keyman_root, ParamStr(0).Substring(0, keyman_root.Length)) then
begin
// Source repo, bin folder

View file

@ -1,18 +1,18 @@
(*
Name: UserMessages
Copyright: Copyright (C) SIL International.
Documentation:
Description:
Documentation:
Description:
Create Date: 5 Jul 2012
Modified Date: 6 Nov 2015
Authors: mcdurdin
Related Files:
Dependencies:
Related Files:
Dependencies:
Bugs:
Todo:
Notes:
Bugs:
Todo:
Notes:
History: 05 Jul 2012 - mcdurdin - I3390 - Font helper can crash when WM_FONTCHANGE received [CrashID:keyman.exe_8.0.350.0_2C53A3AE_EAccessViolation]
04 Nov 2012 - mcdurdin - I3519 - V9.0 - Merge of I3390 - Font helper can crash when WM_FONTCHANGE received
28 May 2014 - mcdurdin - I4242 - Crash when OSK closed/reopened without dismissing hint window [CrashID:keyman.exe_9.0.449.0_2C405C5D_EInvalidPointer]
@ -57,9 +57,14 @@ const
WM_USER_SYNTAXCOLOURCHANGE = WM_USER + 130;
const
// UfrmOSKOnScreenKeyboard
// UfrmPrintOSK
WM_USER_PrintKeyboard = WM_USER + 140;
const
// UfrmOSKOnScreenKeyboard
// Receive notification of modifier key changes from keyman32.dll
WM_KEYMAN_OSK_MODIFIER_EVENT = WM_USER + 141;
const
// UfrmOSKFontHelper
WM_USER_FontChange = WM_USER + 150; // I3390 // I3519

View file

@ -455,6 +455,7 @@ type
procedure DoSaveJSON(ARoot: TJSONObject); virtual;
procedure DoLoadIni(ini: TIniFile); virtual;
procedure DoSaveIni(ini: TIniFile); virtual;
procedure Cleanup; virtual;
public
Options: TPackageOptions;
StartMenu: TPackageStartMenu;
@ -635,6 +636,11 @@ begin
Result := ReplaceStr(ReplaceStr(Trim(VarToStr(v)), #$D#$A, #$A), #$A, #$D#$A);
end;
function TransformSlashes(filename: string): string;
begin
Result := ReplaceStr(filename, '/', '\');
end;
{-------------------------------------------------------------------------------
- TPackageOptions -
------------------------------------------------------------------------------}
@ -697,10 +703,10 @@ procedure TPackageOptions.LoadXML(ARoot: IXMLNode);
begin
FileVersion := XmlVarToStr(ARoot.ChildNodes['System'].ChildNodes['FileVersion'].NodeValue);
ExecuteProgram := XmlVarToStr(ARoot.ChildNodes['Options'].ChildNodes['ExecuteProgram'].NodeValue);
ReadmeFile := Package.Files.FromFileName(XmlVarToStr(ARoot.ChildNodes['Options'].ChildNodes['ReadMeFile'].NodeValue));
GraphicFile := Package.Files.FromFileName(XmlVarToStr(ARoot.ChildNodes['Options'].ChildNodes['GraphicFile'].NodeValue));
LicenseFile := Package.Files.FromFileName(XmlVarToStr(ARoot.ChildNodes['Options'].ChildNodes['LicenseFile'].NodeValue));
WelcomeFile := Package.Files.FromFileName(XmlVarToStr(ARoot.ChildNodes['Options'].ChildNodes['WelcomeFile'].NodeValue));
ReadmeFile := Package.Files.FromFileName(TransformSlashes(XmlVarToStr(ARoot.ChildNodes['Options'].ChildNodes['ReadMeFile'].NodeValue)));
GraphicFile := Package.Files.FromFileName(TransformSlashes(XmlVarToStr(ARoot.ChildNodes['Options'].ChildNodes['GraphicFile'].NodeValue)));
LicenseFile := Package.Files.FromFileName(TransformSlashes(XmlVarToStr(ARoot.ChildNodes['Options'].ChildNodes['LicenseFile'].NodeValue)));
WelcomeFile := Package.Files.FromFileName(TransformSlashes(XmlVarToStr(ARoot.ChildNodes['Options'].ChildNodes['WelcomeFile'].NodeValue)));
if Assigned(ReadmeFile) then ReadmeFile.AddNotifyObject(ReadmeRemoved);
if Assigned(GraphicFile) then GraphicFile.AddNotifyObject(GraphicRemoved);
if Assigned(LicenseFile) then LicenseFile.AddNotifyObject(LicenseRemoved);
@ -1425,7 +1431,7 @@ begin
with ANode.ChildNodes[i] do
begin
subfile := TPackageContentFile.Create(Package);
subfile.FileName := XmlVarToStr(ChildNodes['Name'].NodeValue);
subfile.FileName := TransformSlashes(XmlVarToStr(ChildNodes['Name'].NodeValue));
subfile.Description := XmlVarToStr(ChildNodes['Description'].NodeValue);
subfile.FCopyLocation := TPackageFileCopyLocation(StrToIntDef(XmlVarToStr(ChildNodes['Location'].NodeValue), 0));
Add(subfile);
@ -1884,11 +1890,18 @@ begin
end;
end;
procedure TPackage.Cleanup;
begin
// No op in basic package
end;
procedure TPackage.SaveXML;
var
doc: IXMLDocument;
root: IXMLNode;
begin
Cleanup;
doc := NewXMLDocument;
doc.Encoding := 'utf-8';
@ -1909,6 +1922,8 @@ var
doc: IXMLDocument;
root: IXMLNode;
begin
Cleanup;
doc := NewXMLDocument;
doc.Encoding := 'utf-8';
@ -2238,8 +2253,8 @@ begin
keyboard := TPackageKeyboard.Create(Package);
keyboard.ID := XmlVarToStr(AKeyboard.ChildValues[SXML_PackageKeyboard_ID]);
keyboard.OSKFont := Package.Files.FromFileNameEx(XmlVarToStr(AKeyboard.ChildValues[SXML_PackageKeyboard_OSKFont]));
keyboard.DisplayFont := Package.Files.FromFileNameEx(XmlVarToStr(AKeyboard.ChildValues[SXML_PackageKeyboard_DisplayFont]));
keyboard.OSKFont := Package.Files.FromFileNameEx(TransformSlashes(XmlVarToStr(AKeyboard.ChildValues[SXML_PackageKeyboard_OSKFont])));
keyboard.DisplayFont := Package.Files.FromFileNameEx(TransformSlashes(XmlVarToStr(AKeyboard.ChildValues[SXML_PackageKeyboard_DisplayFont])));
keyboard.Languages.LoadXML(AKeyboard);
keyboard.Examples.LoadXML(AKeyboard);
@ -2877,7 +2892,7 @@ begin
for i := 0 to ARoot.ChildNodes.Count - 1 do
begin
ANode := ARoot.ChildNodes[i];
f := Package.Files.FromFileNameEx(ANode.Attributes[SXML_PackageKeyboardFont_Filename]);
f := Package.Files.FromFileNameEx(TransformSlashes(XmlVarToStr(ANode.Attributes[SXML_PackageKeyboardFont_Filename])));
if Assigned(f) then
Add(f);
end;

View file

@ -39,6 +39,7 @@ uses
DevInstallPackages,
DevIncludePaths,
DevReleaseBuildCheck,
Keyman.System.DevTools.BuildLocaleIndex,
Keyman.System.DevTools.BuildMessageConstants,
Keyman.System.DevTools.BuildSetupStringTranslations,
RegistryKeys;
@ -75,6 +76,7 @@ begin
writeln(' -git : check git repository commit/update status'); // I3726 // I5087 //TODO: remove as unused
writeln(' -buildmessageconstants <strings.xml> <messageidentifierconsts.pas>: build MessageIdentifierConsts.pas from current strings.xml');
writeln(' -buildsetupstrings <sourcepath> <destinationpath>: build Keyman.Setup.System.Locale.*.pas from strings.xml found in sourcepath');
writeln(' -buildlocaleindex <locale-folder> <destination.xml>: build index.xml from strings.xml found in locale-folder');
ExitCode := 1;
Exit;
end;
@ -97,6 +99,8 @@ begin
Success := TBuildMessageConstants.Run(ParamStr(2), ParamStr(3))
else if (ParamStr(1) = '-buildsetupstrings') and (ParamCount = 3) then
Success := TBuildSetupStringTranslations.Run(ParamStr(2), ParamStr(3))
else if (ParamStr(1) = '-buildlocaleindex') and (ParamCount = 3) then
Success := TBuildLocaleIndex.Run(ParamStr(2), ParamStr(3))
else
begin
writeln('Invalid parameters');

View file

@ -0,0 +1,138 @@
(*
* Keyman is copyright (C) SIL Global. MIT License.
*
* Build index.xml from individual locale.xml files
*)
unit Keyman.System.DevTools.BuildLocaleIndex;
interface
uses
System.Classes,
System.SysUtils,
Xml.XMLIntf,
Xml.XMLDoc;
type
TBuildLocaleIndex = class
private
class function ProcessFile(const SourceFile: string; LocalesNode: IXmlNode;
const BCP47: string): Boolean; static;
public
class function Run(SourcePath, Index: string): Boolean; static;
end;
implementation
uses
Xml.Win.msxmldom,
Winapi.ActiveX,
System.StrUtils,
System.TypInfo,
System.Variants;
{ TBuildLocaleIndex }
(**
* Processes a single `strings.xml` file.
*
* Extracts the values of `SKUILanguageName` and
* `SKUILanguageNameWithEnglish`, and appends a `<locale>` entry to the
* given XML node.
*
* @param SourceFile Full path to the `strings.xml` file to process.
* @param LocalesNode The `<locales>` XML node to append to.
* @param BCP47 The BCP-47 language tag.
* @return True if successful; False if required strings were missing.
*)
class function TBuildLocaleIndex.ProcessFile(const SourceFile: string; LocalesNode: IXmlNode;
const BCP47: string): Boolean;
var
doc: IXmlDocument;
node, locale: IXMLNode;
uiLanguageName, uiLanguageNameWithEnglish: string;
begin
doc := TXMLDocument.Create(nil);
doc.ParseOptions := [poResolveExternals]; // I902 - resolve externals when loading XML files
doc.LoadFromFile(SourceFile);
node := doc.DocumentElement.ChildNodes[0];
while Assigned(node) do
begin
if (node.NodeName = 'string') and (node.Attributes['name'] = 'SKUILanguageName') then
uiLanguageName := node.NodeValue
else if (node.NodeName = 'string') and (node.Attributes['name'] = 'SKUILanguageNameWithEnglish') then
uiLanguageNameWithEnglish := node.NodeValue;
node := node.NextSibling;
end;
if (uiLanguageName = '') or (uiLanguageNameWithEnglish = '') then
begin
writeln('ERROR: '+SourceFile+' is missing SKUILanguageName or SKUILanguageNameWithEnglish strings');
Exit(False);
end;
locale := LocalesNode.AddChild('locale');
locale.Attributes['SKLanguageCode'] := BCP47;
locale.Attributes['SKUILanguageName'] := uiLanguageName;
locale.Attributes['SKUILanguageNameWithEnglish'] := uiLanguageNameWithEnglish;
Result := True;
end;
(**
* Finds all localizations and generates an index.xml of the names and
* language codes for each localization, for performance purposes.
*
* @param SourcePath Full path to the locale folder with subfolders
for each localization.
* @param Index Path to the index.xml file to write.
* @return True if successful; False if required strings were missing.
*)
class function TBuildLocaleIndex.Run(SourcePath, Index: string): Boolean;
var
f: TSearchRec;
SourceFile, BCP47: string;
OutputDoc: IXmlDocument;
LocalesNode: IXmlNode;
begin
SourcePath := IncludeTrailingPathDelimiter(SourcePath);
// Iterate through SourcePath/*/strings.xml and generate index.xml
CoInitializeEx(nil, COINIT_APARTMENTTHREADED);
try
Xml.Win.msxmldom.MSXMLDOMDocumentFactory.AddDOMProperty('ProhibitDTD', False);
OutputDoc := NewXMLDocument;
OutputDoc.Encoding := 'UTF-8';
OutputDoc.Active := True;
LocalesNode := OutputDoc.AddChild('locales');
if FindFirst(SourcePath+'*', faDirectory, f) = 0 then
begin
repeat
BCP47 := f.Name;
SourceFile := SourcePath+BCP47+'\strings.xml';
if (BCP47 <> '..') and (BCP47 <> '.') and FileExists(SourcePath+BCP47+'\strings.xml') then
begin
if not ProcessFile(SourceFile, LocalesNode, BCP47) then
Exit(False);
end;
until FindNext(f) <> 0;
FindClose(f);
end;
LocalesNode := nil;
OutputDoc.SaveToFile(Index);
OutputDoc := nil;
finally
CoUninitialize;
end;
Result := True;
end;
end.

View file

@ -16,13 +16,13 @@ builder_parse "$@"
source "$KEYMAN_ROOT/resources/build/win/environment.inc.sh"
builder_describe_internal_dependency \
prepublish:project build:project
builder_describe_outputs \
configure:project /resources/build/win/delphi_environment_generated.inc.sh \
build:project /common/windows/delphi/tools/devtools/$WIN32_TARGET_PATH/devtools.exe
builder_describe_internal_dependency \
prepublish:project build:project
#-------------------------------------------------------------------------------------------------------------------
builder_run_action clean:project clean_windows_project_files

View file

@ -5,24 +5,25 @@ program devtools;
{$R *.res}
uses
System.SysUtils,
DevIncludePaths in 'DevIncludePaths.pas',
DevInstallPackages in 'DevInstallPackages.pas',
DevUtils in 'DevUtils.pas',
DevReleaseBuildCheck in 'DevReleaseBuildCheck.pas',
DevCheckGitStatus in 'DevCheckGitStatus.pas',
RegistryKeys in '..\..\..\..\..\common\windows\delphi\general\RegistryKeys.pas',
KeymanVersion in '..\..\..\..\..\common\windows\delphi\general\KeymanVersion.pas',
utilsystem in '..\..\..\..\..\common\windows\delphi\general\utilsystem.pas',
utilexecute in '..\..\..\..\..\common\windows\delphi\general\utilexecute.pas',
Unicode in '..\..\..\..\..\common\windows\delphi\general\Unicode.pas',
GetOsVersion in '..\..\..\..\..\common\windows\delphi\general\GetOsVersion.pas',
SourceRootPath in 'SourceRootPath.pas',
Keyman.System.DevTools.BuildMessageConstants in 'Keyman.System.DevTools.BuildMessageConstants.pas',
Keyman.System.DevTools.BuildSetupStringTranslations in 'Keyman.System.DevTools.BuildSetupStringTranslations.pas',
SetupStrings in '..\..\..\..\..\windows\src\desktop\setup\SetupStrings.pas',
Keyman.System.AndroidStringToKeymanLocaleString in '..\..\..\..\..\common\windows\delphi\general\Keyman.System.AndroidStringToKeymanLocaleString.pas';
System.SysUtils,
DevIncludePaths in 'DevIncludePaths.pas',
DevInstallPackages in 'DevInstallPackages.pas',
DevUtils in 'DevUtils.pas',
DevReleaseBuildCheck in 'DevReleaseBuildCheck.pas',
DevCheckGitStatus in 'DevCheckGitStatus.pas',
RegistryKeys in '..\..\general\RegistryKeys.pas',
KeymanVersion in '..\..\general\KeymanVersion.pas',
utilsystem in '..\..\general\utilsystem.pas',
utilexecute in '..\..\general\utilexecute.pas',
Unicode in '..\..\general\Unicode.pas',
GetOsVersion in '..\..\general\GetOsVersion.pas',
SourceRootPath in 'SourceRootPath.pas',
Keyman.System.DevTools.BuildMessageConstants in 'Keyman.System.DevTools.BuildMessageConstants.pas',
Keyman.System.DevTools.BuildSetupStringTranslations in 'Keyman.System.DevTools.BuildSetupStringTranslations.pas',
SetupStrings in '..\..\..\..\..\windows\src\desktop\setup\SetupStrings.pas',
Keyman.System.AndroidStringToKeymanLocaleString in '..\..\general\Keyman.System.AndroidStringToKeymanLocaleString.pas',
Keyman.System.DevTools.BuildLocaleIndex in 'Keyman.System.DevTools.BuildLocaleIndex.pas';
begin
try
DevUtils.Run;

File diff suppressed because it is too large Load diff

View file

@ -7,19 +7,20 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
builder_describe "Tool for validating klog is disabled for release builds" \
clean configure build test prepublish edit
builder_parse "$@"
#-------------------------------------------------------------------------------------------------------------------
source "$KEYMAN_ROOT/resources/build/win/environment.inc.sh"
builder_describe_internal_dependency \
prepublish:project build:project
builder_describe_outputs \
configure:project /resources/build/win/delphi_environment_generated.inc.sh \
build:project /common/windows/delphi/tools/test-klog/$WIN32_TARGET_PATH/test_klog.exe
builder_describe_internal_dependency \
prepublish:project build:project
#-------------------------------------------------------------------------------------------------------------------
function do_build() {

View file

@ -4,8 +4,8 @@ rem Build agents have a separate signtime script. We don't use 'call' here so
rem executing that script will terminate this one.
if exist c:\codesign\signtime.bat c:\codesign\signtime.bat %1 %2 %3 %4 %5 %6 %7 %8
set SERVERLIST=(http://timestamp.comodoca.com/authenticode http://timestamp.verisign.com/scripts/timstamp.dll http://timestamp.globalsign.com/scripts/timestamp.dll http://tsa.starfieldtech.com)
set RFC3161SERVERLIST=(http://timestamp.comodoca.com/rfc3161)
set SERVERLIST=(http://timestamp.digicert.com http://timestamp.sectigo.com)
set RFC3161SERVERLIST=(http://timestamp.digicert.com http://timestamp.sectigo.com)
set SIGNTOOL=%1
set PFX_SHA1=%2
set PFX_SHA256=%3

View file

@ -83,7 +83,7 @@ if "!COMMAND!" == "configure" (
if "%1" == "--no-tests" (
meson setup build\!ARCH!\!BUILDTYPE! !STATIC_LIBRARY! --buildtype !BUILDTYPE! -Dkeyman_core_tests=false --werror %2 %3 %4 %5 %6 %7 %8 %9 || exit !errorlevel!
) else (
meson setup build\!ARCH!\!BUILDTYPE! !STATIC_LIBRARY! --buildtype !BUILDTYPE! --werror %1 %2 %3 %4 %5 %6 %7 %8 %9 || exit !errorlevel!
meson setup build\!ARCH!\!BUILDTYPE! !STATIC_LIBRARY! --buildtype !BUILDTYPE! -Dkeyman_core_tests=true --werror %1 %2 %3 %4 %5 %6 %7 %8 %9 || exit !errorlevel!
)
shift
)

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