mirror of
https://github.com/keymanapp/keyman.git
synced 2026-10-28 08:47:47 +00:00
Merge branch 'feat/developer/kmc-convert-createSeperateUtil' of https://github.com/keymanapp/keyman into feat/developer/kmc-convert-createSeperateUtil
This commit is contained in:
commit
8cffd1b18d
623 changed files with 28509 additions and 13934 deletions
10
.github/workflows/api-verification.yml
vendored
10
.github/workflows/api-verification.yml
vendored
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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: |
|
||||
|
|
|
|||
79
.github/workflows/build-test-publish-docker.yml
vendored
Normal file
79
.github/workflows/build-test-publish-docker.yml
vendored
Normal 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
45
.github/workflows/cleanup-ghcr.yml
vendored
Normal 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 }}
|
||||
6
.github/workflows/deb-packaging.README.md
vendored
6
.github/workflows/deb-packaging.README.md
vendored
|
|
@ -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
|
||||
```
|
||||
|
|
|
|||
76
.github/workflows/deb-packaging.yml
vendored
76
.github/workflows/deb-packaging.yml
vendored
|
|
@ -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
153
.github/workflows/npm-publish.yml
vendored
Normal 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"
|
||||
62
.github/workflows/pr-build-status.yml
vendored
62
.github/workflows/pr-build-status.yml
vendored
|
|
@ -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]);
|
||||
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
|
|
|
|||
435
HISTORY.md
435
HISTORY.md
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
19.0.107
|
||||
19.0.171
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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+
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 -->
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
#
|
||||
|
|
|
|||
23
android/docs/engine/KMAPro/index.md
Normal file
23
android/docs/engine/KMAPro/index.md
Normal 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);
|
||||
```
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -55,3 +55,5 @@ or
|
|||
## See also
|
||||
|
||||
- [applyKeyboardHeight()](applyKeyboardHeight)
|
||||
- [getKeyboardHeightMax](getKeyboardHeightMax)
|
||||
- [getKeyboardHeightMin](getKeyboardHeightMin)
|
||||
|
|
|
|||
57
android/docs/engine/KMManager/getKeyboardHeightMax.md
Normal file
57
android/docs/engine/KMManager/getKeyboardHeightMax.md
Normal 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)
|
||||
57
android/docs/engine/KMManager/getKeyboardHeightMin.md
Normal file
57
android/docs/engine/KMManager/getKeyboardHeightMin.md
Normal 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)
|
||||
49
android/docs/engine/KMManager/getNavigationBarHeight.md
Normal file
49
android/docs/engine/KMManager/getNavigationBarHeight.md
Normal 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)
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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/)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
28
android/docs/help/about/adware.md
Normal file
28
android/docs/help/about/adware.md
Normal 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.
|
||||
|
||||

|
||||
|
||||
> 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/).
|
||||
|
|
@ -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)
|
||||
BIN
android/docs/help/android_images/attention.png
Normal file
BIN
android/docs/help/android_images/attention.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
19
common/schemas/model_info/README.md
Normal file
19
common/schemas/model_info/README.md
Normal 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
|
||||
49
common/schemas/model_info/model_info.schema.json
Normal file
49
common/schemas/model_info/model_info.schema.json
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
499
common/test/keyboards/platform-rules/platformtest.js
Normal file
499
common/test/keyboards/platform-rules/platformtest.js
Normal 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;
|
||||
};
|
||||
}
|
||||
BIN
common/test/keyboards/platform-rules/platformtest.kmp
Normal file
BIN
common/test/keyboards/platform-rules/platformtest.kmp
Normal file
Binary file not shown.
BIN
common/test/keyboards/platform-rules/platformtest.kmx
Normal file
BIN
common/test/keyboards/platform-rules/platformtest.kmx
Normal file
Binary file not shown.
|
|
@ -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>
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
store(&TARGETS) 'any'
|
||||
store(&VERSION) '10.0'
|
||||
store(&TARGETS) 'any'
|
||||
store(&NAME) 'PlatformTest'
|
||||
store(&KEYBOARDVERSION) '1.0'
|
||||
store(©RIGHT) '© SIL Global'
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
BIN
common/test/keyboards/platform-rules/source/platformtest.kmp
Normal file
BIN
common/test/keyboards/platform-rules/source/platformtest.kmp
Normal file
Binary file not shown.
BIN
common/test/keyboards/platform-rules/source/platformtest.kmx
Normal file
BIN
common/test/keyboards/platform-rules/source/platformtest.kmx
Normal file
Binary file not shown.
|
|
@ -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>
|
||||
BIN
common/test/keyboards/test9469/build/test9469.kmp
Normal file
BIN
common/test/keyboards/test9469/build/test9469.kmp
Normal file
Binary file not shown.
BIN
common/test/keyboards/test9469/build/test9469.kmx
Normal file
BIN
common/test/keyboards/test9469/build/test9469.kmx
Normal file
Binary file not shown.
BIN
common/test/keyboards/test9469/build/test9469.kvk
Normal file
BIN
common/test/keyboards/test9469/build/test9469.kvk
Normal file
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
|
@ -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(©RIGHT) '© 2023 SIL International'
|
||||
store(&KEYBOARDVERSION) '1.1'
|
||||
|
|
@ -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>
|
||||
|
|
@ -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.)
|
||||
8
common/test/resources/mocha-teamcity-reporter/README.md
Normal file
8
common/test/resources/mocha-teamcity-reporter/README.md
Normal 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.
|
||||
259
common/test/resources/mocha-teamcity-reporter/teamcity.cjs
Normal file
259
common/test/resources/mocha-teamcity-reporter/teamcity.cjs
Normal 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;
|
||||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
".": {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
0
common/web/types/src/util/kmcConvertUtil.ts
Normal file
0
common/web/types/src/util/kmcConvertUtil.ts
Normal 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);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Reference in a new issue