mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-05 08:25:32 +00:00
Merge branch 'master' into chore/developer/scatteredCode/cleanup_VKeys
Some checks are pending
Keyman Build Summary / Summarize build status checks (push) Waiting to run
Some checks are pending
Keyman Build Summary / Summarize build status checks (push) Waiting to run
This commit is contained in:
commit
9ca40faac7
2290 changed files with 75763 additions and 37001 deletions
1
.gitattributes
vendored
1
.gitattributes
vendored
|
|
@ -7,3 +7,4 @@
|
|||
# EOF
|
||||
# ```
|
||||
Dockerfile text eol=lf
|
||||
*.sh text eol=lf
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ runs:
|
|||
using: 'composite'
|
||||
steps:
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: keyman-srcpkg
|
||||
path: artifacts/keyman-srcpkg
|
||||
|
|
@ -49,7 +49,7 @@ runs:
|
|||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Store binary packages
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: keyman-binarypkgs-${{ inputs.dist }}_${{ inputs.arch }}
|
||||
path: |
|
||||
|
|
|
|||
195
.github/labeler.yml
vendored
195
.github/labeler.yml
vendored
|
|
@ -5,86 +5,173 @@
|
|||
# common ones. The others are commented out. There is still some variance between
|
||||
# folder names and labels; consider this documentation of that ;-)
|
||||
|
||||
docs: docs/**
|
||||
docs:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: 'docs/**'
|
||||
|
||||
#
|
||||
# Add labels based on changed files using actions/labeler
|
||||
#
|
||||
|
||||
android/:
|
||||
- android/**
|
||||
- resources/teamcity/includes/**
|
||||
- resources/teamcity/android/**
|
||||
android/app/: android/KMAPro/**
|
||||
android/engine/: android/KMEA/**
|
||||
android/samples/: android/Samples/**
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- android/**
|
||||
- resources/teamcity/includes/**
|
||||
- resources/teamcity/android/**
|
||||
android/app/:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- android/KMAPro/**
|
||||
android/engine/:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- android/KMEA/**
|
||||
android/samples/:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- android/Samples/**
|
||||
|
||||
common/:
|
||||
- common/**
|
||||
- resources/teamcity/includes/**
|
||||
- resources/teamcity/common/**
|
||||
common/web/: common/web/**
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- common/**
|
||||
- resources/teamcity/includes/**
|
||||
- resources/teamcity/common/**
|
||||
common/web/:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- common/web/**
|
||||
|
||||
core/:
|
||||
- core/**
|
||||
- resources/teamcity/includes/**
|
||||
- resources/teamcity/core/**
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- core/**
|
||||
- resources/teamcity/includes/**
|
||||
- resources/teamcity/core/**
|
||||
|
||||
developer/:
|
||||
- developer/**
|
||||
- resources/teamcity/includes/**
|
||||
- resources/teamcity/developer/**
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- developer/**
|
||||
- resources/teamcity/includes/**
|
||||
- resources/teamcity/developer/**
|
||||
developer/compilers/:
|
||||
- developer/src/kmc/**
|
||||
- developer/src/kmcmplib/**
|
||||
- developer/src/kmc-*/**
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- developer/src/kmc/**
|
||||
- developer/src/kmcmplib/**
|
||||
- developer/src/kmc-*/**
|
||||
developer/ide/:
|
||||
- developer/src/server/**
|
||||
- developer/src/tike/**
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- developer/src/server/**
|
||||
- developer/src/tike/**
|
||||
|
||||
ios/:
|
||||
- ios/**
|
||||
- resources/teamcity/includes/**
|
||||
- resources/teamcity/ios/**
|
||||
ios/app/: ios/keyman/**
|
||||
ios/engine/: ios/engine/**
|
||||
ios/samples/: ios/samples/**
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- ios/**
|
||||
- resources/teamcity/includes/**
|
||||
- resources/teamcity/ios/**
|
||||
ios/app/:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- ios/keyman/**
|
||||
ios/engine/:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- ios/engine/**
|
||||
ios/samples/:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- ios/samples/**
|
||||
|
||||
linux/:
|
||||
- linux/**
|
||||
- resources/teamcity/includes/**
|
||||
- resources/teamcity/linux/**
|
||||
linux/config/: linux/keyman-config/**
|
||||
linux/engine/: linux/ibus-keyman/**
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- linux/**
|
||||
- resources/teamcity/includes/**
|
||||
- resources/teamcity/linux/**
|
||||
linux/config/:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- linux/keyman-config/**
|
||||
linux/engine/:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- linux/ibus-keyman/**
|
||||
|
||||
mac/:
|
||||
- mac/**
|
||||
- resources/teamcity/includes/**
|
||||
- resources/teamcity/mac/**
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- mac/**
|
||||
- resources/teamcity/includes/**
|
||||
- resources/teamcity/mac/**
|
||||
# mac/config/:
|
||||
# mac/engine/: mac/**
|
||||
|
||||
oem/: oem/**
|
||||
oem/fv/: oem/firstvoices/**
|
||||
oem/fv/android/: oem/firstvoices/android/**
|
||||
oem/fv/ios/: oem/firstvoices/ios/**
|
||||
oem/fv/windows/: oem/firstvoices/windows/**
|
||||
oem/:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- oem/**
|
||||
oem/fv/:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- oem/firstvoices/**
|
||||
oem/fv/android/:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- oem/firstvoices/android/**
|
||||
oem/fv/ios/:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- oem/firstvoices/ios/**
|
||||
oem/fv/windows/:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- oem/firstvoices/windows/**
|
||||
|
||||
resources/: resources/**
|
||||
resources/:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- resources/**
|
||||
|
||||
web/:
|
||||
- web/**
|
||||
- resources/teamcity/includes/**
|
||||
- resources/teamcity/web/**
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- web/**
|
||||
- resources/teamcity/includes/**
|
||||
- resources/teamcity/web/**
|
||||
# web/bookmarklet/
|
||||
web/engine/: web/source/**
|
||||
web/ui/: web/source/kmwui*
|
||||
web/samples/: web/samples/**
|
||||
web/predictive-text/: web/src/engine/predictive-text/**
|
||||
web/engine/:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- web/source/**
|
||||
web/ui/:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- web/source/kmwui*
|
||||
web/samples/:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- web/samples/**
|
||||
web/predictive-text/:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- web/src/engine/predictive-text/**
|
||||
|
||||
windows/:
|
||||
- windows/**
|
||||
- resources/teamcity/includes/**
|
||||
- resources/teamcity/windows/**
|
||||
windows/config/: windows/src/desktop/**
|
||||
windows/engine/: windows/src/engine/**
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- windows/**
|
||||
- resources/teamcity/includes/**
|
||||
- resources/teamcity/windows/**
|
||||
windows/config/:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- windows/src/desktop/**
|
||||
windows/engine/:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- windows/src/engine/**
|
||||
|
|
|
|||
144
.github/workflows/README.md
vendored
Normal file
144
.github/workflows/README.md
vendored
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
# api-verification.yml: checks Core API for incompatible changes
|
||||
|
||||
This action automatically verifies that the API of Keyman Core did not
|
||||
change in incompatible ways without incrementing the API version number.
|
||||
It applies to all platforms that use Core, even though we only run it on
|
||||
Linux.
|
||||
|
||||
- The action gets triggered by the completion of the Ubuntu packaging
|
||||
workflow (`deb-packaging.yml`).
|
||||
- The API version is defined in `core/CORE_API_VERSION.md`.
|
||||
- The baseline file for comparing the API is
|
||||
`linux/debian/libkeymancore${API_VERSION}.symbols`. The action will
|
||||
re-create this file and and makes it available as an artifact.
|
||||
- The API checks are implemented in `linux/scripts/verify_api.inc.sh`.
|
||||
|
||||
More information about the API verification can be found in
|
||||
`docs/core-api-verification.md`.
|
||||
|
||||
# auto-merge-keyman-server-pr.yml
|
||||
TODO
|
||||
|
||||
# build-test-publish-docker.yml
|
||||
TODO
|
||||
|
||||
# cleanup-ghcr.yml
|
||||
TODO
|
||||
|
||||
# close-linked-issues-for-merged-prs.yml
|
||||
TODO
|
||||
|
||||
# core-arm64-windows-test.yml: Test Keyman Core on Windows/ARM64
|
||||
|
||||
This action allows us to test Keyman Core for Windows on ARM64 architecture with
|
||||
a GitHub agent, as we do not have any TeamCity Windows build agents with ARM64
|
||||
arch.
|
||||
|
||||
It will normally be triggered as part of a test build from the
|
||||
resources/teamcity/triggers/trigger-test-builds.sh script.
|
||||
|
||||
You can manually trigger a core-arm64-windows-test action on GitHub on your fork
|
||||
of keymanapp/keyman. This is normally only needed to test changes to the action
|
||||
itself:
|
||||
|
||||
- push the changes to your fork of keymanapp/keyman: `git push myfork HEAD:master`
|
||||
- make sure you have GHA enabled in the settings of your fork
|
||||
- seet `GITHUB_TOKEN=<your_gh_token>`
|
||||
- trigger a build with:
|
||||
|
||||
```bash
|
||||
curl --write-out '\n' --request POST \
|
||||
--header "Accept: application/vnd.github+json" \
|
||||
--header "Authorization: token $GITHUB_TOKEN" \
|
||||
--data "{ \"event_type\": \"core-arm64-windows-test: master\", \
|
||||
\"client_payload\": { \
|
||||
\"buildSha\": \"$(git rev-parse refs/heads/master)\", \
|
||||
\"branch\": \"master\", \
|
||||
\"user\": \"${USER}\", \
|
||||
\"isTestBuild\": \"true\", \
|
||||
\"force\": \"true\"}" \
|
||||
https://api.github.com/repos/<yourgithubname>/keyman/dispatches
|
||||
```
|
||||
|
||||
To trigger a build for PR #1234 in a branch `my-pr-branch` this would look similar
|
||||
to this:
|
||||
|
||||
```bash
|
||||
curl --write-out '\n' --request POST \
|
||||
--header "Accept: application/vnd.github+json" \
|
||||
--header "Authorization: token $GITHUB_TOKEN" \
|
||||
--data "{ \"event_type\": \"core-arm64-windows-test: PR #1234\", \
|
||||
\"client_payload\": { \
|
||||
\"buildSha\": \"$(git rev-parse refs/heads/my-pr-branch)\", \
|
||||
\"branch\": \"my-pr-branch\", \
|
||||
\"user\": \"${USER}\", \
|
||||
\"isTestBuild\": \"true\", \
|
||||
\"force\": \"true\"}" \
|
||||
https://api.github.com/repos/<yourgithubname>/keyman/dispatches
|
||||
```
|
||||
|
||||
....
|
||||
|
||||
# crowdin.yml: Upload translation sources to [Crowdin](translate.keyman.com)
|
||||
|
||||
This action automatically updates the English source strings on Crowdin
|
||||
https://crowdin.com/project/keyman/sources/files
|
||||
|
||||
based on the root [crowdin.yml](../../crowdin.yml) configuration file. The daily cron job currently runs at 6:00AM.
|
||||
|
||||
This should match the behavior of locally using the [Crowdin CLI to upload sources](https://crowdin.github.io/crowdin-cli/commands/crowdin-upload-sources).
|
||||
|
||||
# deb-packaging.yml: Ubuntu Packaging GitHub Action
|
||||
|
||||
You can manually trigger a deb-packaging action on GitHub on your fork
|
||||
of keymanapp/keyman. This is normally only needed to test changes to the action
|
||||
itself:
|
||||
|
||||
- push the changes to your fork of keymanapp/keyman: `git push myfork HEAD:master`
|
||||
- make sure you have GHA enabled in the settings of your fork
|
||||
- seet `GITHUB_TOKEN=<your_gh_token>`
|
||||
- trigger a build with:
|
||||
|
||||
```bash
|
||||
curl --write-out '\n' --request POST \
|
||||
--header "Accept: application/vnd.github+json" \
|
||||
--header "Authorization: token $GITHUB_TOKEN" \
|
||||
--data "{ \"event_type\": \"deb-pr-packaging: master\", \
|
||||
\"client_payload\": { \
|
||||
\"buildSha\": \"$(git rev-parse refs/heads/master)\", \
|
||||
\"branch\": \"master\", \
|
||||
\"baseBranch\": \"master\", \
|
||||
\"baseRef\": \"$(git rev-parse refs/heads/master^)\", \
|
||||
\"user\": \"${USER}\", \
|
||||
\"isTestBuild\": \"true\", \
|
||||
\"force\": \"true\"}" \
|
||||
https://api.github.com/repos/<yourgithubname>/keyman/dispatches
|
||||
```
|
||||
|
||||
To trigger a build for PR #1234 in a branch `pr-1234` this would look similar
|
||||
to this:
|
||||
|
||||
```bash
|
||||
curl --write-out '\n' --request POST \
|
||||
--header "Accept: application/vnd.github+json" \
|
||||
--header "Authorization: token $GITHUB_TOKEN" \
|
||||
--data "{ \"event_type\": \"deb-pr-packaging: PR #1234\", \
|
||||
\"client_payload\": { \
|
||||
\"buildSha\": \"$(git rev-parse refs/heads/pr-1234)\", \
|
||||
\"branch\": \"pr-1234\", \
|
||||
\"baseBranch\": \"master\", \
|
||||
\"baseRef\": \"$(git rev-parse refs/heads/master)\", \
|
||||
\"user\": \"${USER}\", \
|
||||
\"isTestBuild\": \"true\", \
|
||||
\"force\": \"true\"}" \
|
||||
https://api.github.com/repos/<yourgithubname>/keyman/dispatches
|
||||
```
|
||||
|
||||
# labeler.yml
|
||||
TODO
|
||||
|
||||
# npm-publish.yml
|
||||
TODO
|
||||
|
||||
# pr-build-status.yml
|
||||
TODO
|
||||
20
.github/workflows/api-verification.yml
vendored
20
.github/workflows/api-verification.yml
vendored
|
|
@ -23,10 +23,11 @@ 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
|
||||
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
with:
|
||||
path: |
|
||||
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,7 +54,8 @@ jobs:
|
|||
-f context="$STATUS_CONTEXT"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
if: steps.environment_step.outputs.SKIP_API_CHECK != 'true'
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ref: '${{ steps.environment_step.outputs.GIT_SHA }}'
|
||||
fetch-depth: 0
|
||||
|
|
@ -61,13 +63,15 @@ 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
|
||||
echo "Verify API for libkeymancore*.so (${{ steps.environment_step.outputs.GIT_BRANCH }}, branch ${{ steps.environment_step.outputs.GIT_BASE_BRANCH }}, by ${{ steps.environment_step.outputs.GIT_USER }}):" | tee -a ${GITHUB_STEP_SUMMARY}
|
||||
|
||||
BIN_PACKAGE=$(ls "${GITHUB_WORKSPACE}/artifacts/" | grep "${PKG_NAME}[0-9]*_${{ steps.environment_step.outputs.KEYMAN_VERSION }}-1${{ steps.environment_step.outputs.PRERELEASE_TAG }}+$(lsb_release -c -s)1_amd64.deb")
|
||||
cd ${{ github.workspace }}/keyman/linux
|
||||
|
|
@ -76,20 +80,20 @@ jobs:
|
|||
--bin-pkg "${GITHUB_WORKSPACE}/artifacts/${BIN_PACKAGE}" \
|
||||
--git-sha "${{ steps.environment_step.outputs.GIT_SHA }}" \
|
||||
--git-base "${{ steps.environment_step.outputs.GIT_BASE }}" \
|
||||
verify 2>> $GITHUB_STEP_SUMMARY
|
||||
verify 2> >(tee -a ${GITHUB_STEP_SUMMARY} >&2)
|
||||
|
||||
- name: Archive .symbols file
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
if: steps.environment_step.outputs.SKIP_API_CHECK != 'true' && always()
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
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'
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
#
|
||||
# Automatically merges pull requests opened by keyman-server
|
||||
# The initial use of this action is to increment the patch
|
||||
# version of Keyman with resources/build/increment-version.sh.
|
||||
# That script creates a PR and then this workflow will be
|
||||
# triggered to approve the PR (from github-actions account), and
|
||||
# then automerge it.
|
||||
# Automatically merges pull requests opened by keyman-server The initial use of
|
||||
# this action is to increment the patch version of Keyman with
|
||||
# resources/teamcity/triggers/trigger-release-builds.sh. That script creates a PR and
|
||||
# then this workflow will be triggered to approve the PR (from github-actions
|
||||
# account), and then automerge it.
|
||||
#
|
||||
name: Auto Merge PRs from keyman-server
|
||||
on:
|
||||
|
|
@ -14,15 +13,17 @@ 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
|
||||
uses: hmarr/auto-approve-action@f0939ea97e9205ef24d872e76833fa908a770363
|
||||
with:
|
||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
- name: auto merge PR from keyman-server
|
||||
uses: "pascalgn/automerge-action@7854d3bd607dccdaf0b2c134b699a812c8960213"
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.AUTOINC_GITHUB_TOKEN }}"
|
||||
MERGE_LABELS: "automerge"
|
||||
MERGE_FORKS: false
|
||||
GH_TOKEN: "${{ github.token }}"
|
||||
run: |
|
||||
gh pr review -R keymanapp/keyman --approve ${{ github.event.pull_request.number }}
|
||||
- name: mark PR for auto-merge from keyman-server
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: "${{ secrets.AUTOINC_GITHUB_TOKEN }}"
|
||||
run: |
|
||||
gh pr merge -R keymanapp/keyman --merge --auto ${{ github.event.pull_request.number }}
|
||||
|
|
|
|||
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- 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 }}
|
||||
167
.github/workflows/core-arm64-windows-test.yml
vendored
Normal file
167
.github/workflows/core-arm64-windows-test.yml
vendored
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
#
|
||||
# This workflow tests Keyman Core on ARM64/Windows; we cannot use our TeamCity
|
||||
# agents because we have no ARM64 Windows agents.
|
||||
#
|
||||
# It is triggered by a repository_dispatch event with the type
|
||||
# `core-arm64-windows-test:*`.
|
||||
#
|
||||
|
||||
name: "Keyman Core - ARM64/Windows test"
|
||||
run-name: "Keyman Core - ARM64/Windows test - ${{ github.event.client_payload.branch }} by @${{ github.event.client_payload.user }}"
|
||||
on:
|
||||
repository_dispatch:
|
||||
types: ['core-arm64-windows-test:*']
|
||||
|
||||
# Inputs:
|
||||
#
|
||||
# buildSha: The SHA of the commit to build, e.g. of the branch or
|
||||
# refs/pull/1234/head for PR
|
||||
# isTestBuild: true for PR and test builds, false for release builds
|
||||
# force: true to run this action on a fork of keymanapp/keyman
|
||||
#
|
||||
# Descriptive inputs:
|
||||
#
|
||||
# branch: Name of the branch to build, generally, `master`, `beta`,
|
||||
# `stable-x.y`, or for a PR `#1234`. Used only in the action
|
||||
# title
|
||||
# user: The user that triggered the build or created the PR. Used
|
||||
# only in the action title
|
||||
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
STATUS_CONTEXT: 'Keyman Core - ARM64 test'
|
||||
_builder_timings: false
|
||||
|
||||
jobs:
|
||||
core-arm64-windows-test:
|
||||
name: Keyman Core ARM64/Windows test
|
||||
if: github.repository == 'keymanapp/keyman' || github.event.client_payload.force
|
||||
runs-on: windows-11-arm
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ref: '${{ github.event.client_payload.buildSha }}'
|
||||
|
||||
- name: Set pending status on PR builds
|
||||
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='Keyman Core - ARM64 test started' \
|
||||
-f context="$STATUS_CONTEXT"
|
||||
|
||||
- name: Report initial npm/node versions
|
||||
run: |
|
||||
echo "Initial npm/node versions"
|
||||
npm -v
|
||||
node -v
|
||||
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: '3.13'
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
# This needs to be kept in sync with minimum-versions.sh
|
||||
node-version: '20.19'
|
||||
# As of Dec 2025, ngrok not yet supported on ARM Windows,
|
||||
# google-closure-compiler (and others?) does not support arm64 yet, so
|
||||
# use the x64 version of node. This is needed to avoid `npm ci` failure,
|
||||
# even though we don't use ngrok directly; google-closure-compiler is
|
||||
# used by emscripten
|
||||
architecture: x64
|
||||
|
||||
- name: Report on current environment
|
||||
shell: bash
|
||||
run: |
|
||||
echo "-- Environment --"
|
||||
echo "pythonLocation=$pythonLocation"
|
||||
echo "-- Current npm/node versions --"
|
||||
npm -v
|
||||
node -v
|
||||
|
||||
- name: Setup environment
|
||||
shell: bash
|
||||
run: |
|
||||
# As of Dec 2025, Emscripten does not support Win11 ARM natively
|
||||
echo "EMSDK_ARCH=x86_64" >> $GITHUB_ENV
|
||||
echo "EMSDK_KEEP_DOWNLOADS=1" >> $GITHUB_ENV
|
||||
echo "KEYMAN_USE_EMSDK=1" >> $GITHUB_ENV
|
||||
|
||||
echo "PATH=$PATH:$pythonLocation/Scripts" >> $GITHUB_ENV
|
||||
|
||||
echo "KEYMAN_CI_SKIP_NVM=1" >> $GITHUB_ENV
|
||||
|
||||
|
||||
- name: Install emscripten
|
||||
shell: bash
|
||||
run: |
|
||||
source resources/build/minimum-versions.inc.sh
|
||||
mkdir ../emsdk
|
||||
pushd ../emsdk
|
||||
git clone https://github.com/emscripten-core/emsdk.git
|
||||
cd emsdk
|
||||
./emsdk install "${KEYMAN_MIN_VERSION_EMSCRIPTEN}"
|
||||
./emsdk activate "${KEYMAN_MIN_VERSION_EMSCRIPTEN}"
|
||||
cd upstream/emscripten
|
||||
npm install
|
||||
echo "EMSCRIPTEN_BASE=$(pwd)" >> $GITHUB_ENV
|
||||
popd
|
||||
|
||||
- name: Install meson
|
||||
shell: bash
|
||||
run: |
|
||||
pip3 install --user meson==1.11.0
|
||||
|
||||
- name: Run test
|
||||
shell: bash
|
||||
run: |
|
||||
# Run the test
|
||||
./core/build.sh configure,build,test:arm64
|
||||
|
||||
set_status:
|
||||
name: Set result status on PR builds
|
||||
needs: [core-arm64-windows-test]
|
||||
runs-on: windows-11-arm
|
||||
if: ${{ always() && github.event.client_payload.isTestBuild == 'true' }}
|
||||
steps:
|
||||
- name: Set success
|
||||
if: needs.core-arm64-windows-test.result == 'success'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "RESULT=success" >> $GITHUB_ENV
|
||||
echo "MSG=Keyman Core ARM64 test succeeded" >> $GITHUB_ENV
|
||||
|
||||
- name: Set cancelled
|
||||
if: needs.core-arm64-windows-test.result == 'cancelled'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "RESULT=error" >> $GITHUB_ENV
|
||||
echo "MSG=Keyman Core ARM64 test cancelled" >> $GITHUB_ENV
|
||||
|
||||
- name: Set failure
|
||||
if: needs.core-arm64-windows-test.result == 'failure'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "RESULT=failure" >> $GITHUB_ENV
|
||||
echo "MSG=Keyman Core ARM64 test failed" >> $GITHUB_ENV
|
||||
|
||||
- name: Set final status
|
||||
shell: bash
|
||||
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"
|
||||
4
.github/workflows/crowdin.yml
vendored
4
.github/workflows/crowdin.yml
vendored
|
|
@ -13,10 +13,10 @@ jobs:
|
|||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: crowdin action
|
||||
uses: crowdin/github-action@2.7.0
|
||||
uses: crowdin/github-action@v2.7.0
|
||||
with:
|
||||
upload_sources: true
|
||||
|
||||
|
|
|
|||
41
.github/workflows/deb-packaging.README.md
vendored
41
.github/workflows/deb-packaging.README.md
vendored
|
|
@ -1,41 +0,0 @@
|
|||
# Ubuntu Packaging GitHub Action
|
||||
|
||||
You can manually trigger a deb-packaging action on GitHub, e.g. to test changes:
|
||||
|
||||
- push the changes to your fork of keymanapp/keyman: git push myfork HEAD:master
|
||||
- make sure you have GHA enabled in the settings of your fork and created an
|
||||
access token on GitHub
|
||||
- trigger a build with:
|
||||
|
||||
```bash
|
||||
curl --write-out '\n' --request POST \
|
||||
--header "Accept: application/vnd.github+json" \
|
||||
--header "Authorization: token $GITHUB_TOKEN" \
|
||||
--data "{ \"event_type\": \"deb-pr-packaging: master\", \
|
||||
\"client_payload\": { \
|
||||
\"buildSha\": \"$(git rev-parse refs/heads/master)\", \
|
||||
\"branch\": \"master\", \
|
||||
\"baseBranch\": \"master\",
|
||||
\"baseRef\": \"$(git rev-parse refs/heads/master^)\",
|
||||
\"user\": \"${USER}\",
|
||||
\"isTestBuild\": \"true\"}" \
|
||||
https://api.github.com/repos/<yourgithubname>/keyman/dispatches
|
||||
```
|
||||
|
||||
To trigger a build for PR #1234 in a branch `pr-1234` this would look similar
|
||||
to this:
|
||||
|
||||
```bash
|
||||
curl --write-out '\n' --request POST \
|
||||
--header "Accept: application/vnd.github+json" \
|
||||
--header "Authorization: token $GITHUB_TOKEN" \
|
||||
--data "{ \"event_type\": \"deb-pr-packaging: PR #1234\", \
|
||||
\"client_payload\": { \
|
||||
\"buildSha\": \"$(git rev-parse refs/heads/pr-1234)\", \
|
||||
\"branch\": \"pr-1234\", \
|
||||
\"baseBranch\": \"master\",
|
||||
\"baseRef\": \"$(git rev-parse refs/heads/master)\",
|
||||
\"user\": \"${USER}\",
|
||||
\"isTestBuild\": \"true\"}" \
|
||||
https://api.github.com/repos/<yourgithubname>/keyman/dispatches
|
||||
```
|
||||
209
.github/workflows/deb-packaging.yml
vendored
209
.github/workflows/deb-packaging.yml
vendored
|
|
@ -23,25 +23,28 @@ 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"
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
STATUS_CONTEXT: 'Ubuntu Packaging'
|
||||
STATUS_CONTEXT_SOURCE_VERIFICATION: 'Linux source verification'
|
||||
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 }}
|
||||
PRERELEASE_TAG: ${{ steps.prerelease_tag.outputs.PRERELEASE_TAG }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ref: '${{ github.event.client_payload.buildSha }}'
|
||||
|
||||
|
|
@ -86,7 +89,14 @@ jobs:
|
|||
shell: bash
|
||||
run: |
|
||||
THIS_SCRIPT="$GITHUB_WORKSPACE/.github/workflows/deb-packaging.yml"
|
||||
. "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh"
|
||||
if [[ -f "${THIS_SCRIPT%/*}/../../resources/build/builder-basic.inc.sh" ]]; then
|
||||
. "${THIS_SCRIPT%/*}/../../resources/build/builder-basic.inc.sh"
|
||||
else
|
||||
# TODO: remove following block post v19 release (file was renamed in v19 alpha)
|
||||
if [[ -f "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh" ]]; then
|
||||
. "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh"
|
||||
fi
|
||||
fi
|
||||
echo "KEYMAN_VERSION=${KEYMAN_VERSION:-${VERSION}}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Set prerelease tag as output parameter
|
||||
|
|
@ -112,7 +122,7 @@ jobs:
|
|||
echo "- $(find . -name keyman_\*.dsc)" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Store source package
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: keyman-srcpkg
|
||||
path: |
|
||||
|
|
@ -127,11 +137,11 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
dist: [jammy, noble, plucky]
|
||||
dist: [jammy, noble, questing]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ref: '${{ github.event.client_payload.buildSha }}'
|
||||
sparse-checkout: '.github/actions/'
|
||||
|
|
@ -153,11 +163,11 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
dist: [questing]
|
||||
dist: [devel]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ref: '${{ github.event.client_payload.buildSha }}'
|
||||
sparse-checkout: '.github/actions/'
|
||||
|
|
@ -177,28 +187,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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: keyman-srcpkg
|
||||
path: artifacts
|
||||
|
||||
- name: Download Binary Artifacts
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
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
|
||||
|
|
@ -247,7 +282,7 @@ jobs:
|
|||
echo "::endgroup::"
|
||||
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: keyman-signedpkgs
|
||||
|
||||
|
|
@ -309,7 +344,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
path: artifacts
|
||||
pattern: keyman-*
|
||||
|
|
@ -325,9 +360,10 @@ 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
|
||||
uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
with:
|
||||
path: |
|
||||
artifacts
|
||||
|
|
@ -368,3 +404,112 @@ jobs:
|
|||
-f target_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" \
|
||||
-f description="$MSG" \
|
||||
-f context="$STATUS_CONTEXT"
|
||||
|
||||
#---------------------------------------------------------------------------------------------
|
||||
source_verification:
|
||||
name: Verify source tarball and Linux source package
|
||||
if: github.repository == 'keymanapp/keyman' || github.event.client_payload.force
|
||||
needs: sourcepackage
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
KEYMAN_VERSION: ${{ needs.sourcepackage.outputs.KEYMAN_VERSION }}
|
||||
HAS_VERIFY_SOURCE: ${{ steps.check_requirements.outputs.HAS_VERIFY_SOURCE }}
|
||||
|
||||
steps:
|
||||
- name: Set pending status on PR builds
|
||||
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='Linux source verification started' \
|
||||
-f context="$STATUS_CONTEXT_SOURCE_VERIFICATION"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ref: '${{ github.event.client_payload.buildSha }}'
|
||||
fetch-depth: 0
|
||||
clean: false
|
||||
path: ${{ github.workspace }}/keyman/
|
||||
|
||||
# TODO: Remove after release of Keyman 19
|
||||
- name: Check requirements
|
||||
id: check_requirements
|
||||
shell: bash
|
||||
run: |
|
||||
if [[ -f "${GITHUB_WORKSPACE}/keyman/linux/scripts/verify_source.sh" ]]; then
|
||||
echo "HAS_VERIFY_SOURCE=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Install devscripts
|
||||
if: steps.check_requirements.outputs.HAS_VERIFY_SOURCE == 'true'
|
||||
uses: ./keyman/.github/actions/apt-install
|
||||
with:
|
||||
packages: devscripts equivs
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.check_requirements.outputs.HAS_VERIFY_SOURCE == 'true'
|
||||
run: |
|
||||
cd ${{ github.workspace }}/keyman/linux
|
||||
./scripts/deb-packaging.sh --gha dependencies
|
||||
|
||||
- name: Verify source tarball
|
||||
if: steps.check_requirements.outputs.HAS_VERIFY_SOURCE == 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
cd ${{ github.workspace }}/keyman/linux
|
||||
./scripts/verify_source.sh --source-only --target-dir "${{ github.workspace }}/keyman-${{ needs.sourcepackage.outputs.KEYMAN_VERSION }}"
|
||||
|
||||
- name: Verify Ubuntu source package
|
||||
if: steps.check_requirements.outputs.HAS_VERIFY_SOURCE == 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
cd ${{ github.workspace }}/keyman/linux
|
||||
./scripts/verify_source.sh --no-create-tarball --launchpad-only --target-dir "${{ github.workspace }}/keyman-${{ needs.sourcepackage.outputs.KEYMAN_VERSION }}"
|
||||
|
||||
set_status_source_verification:
|
||||
name: Set result status of source verification on PR builds
|
||||
needs: [source_verification]
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ always() && github.event.client_payload.isTestBuild == 'true' && (github.repository == 'keymanapp/keyman' || github.event.client_payload.force) }}
|
||||
steps:
|
||||
- name: Requirements not met
|
||||
if: needs.source_verification.result == 'success' && needs.source_verification.outputs.HAS_VERIFY_SOURCE != 'true'
|
||||
run: |
|
||||
echo "RESULT=success" >> $GITHUB_ENV
|
||||
echo "MSG=Linux source verification requirements not met" >> $GITHUB_ENV
|
||||
|
||||
- name: Set success
|
||||
if: needs.source_verification.result == 'success' && needs.source_verification.outputs.HAS_VERIFY_SOURCE == 'true'
|
||||
run: |
|
||||
echo "RESULT=success" >> $GITHUB_ENV
|
||||
echo "MSG=Linux source verification succeeded" >> $GITHUB_ENV
|
||||
|
||||
- name: Set cancelled
|
||||
if: needs.source_verification.result == 'cancelled'
|
||||
run: |
|
||||
echo "RESULT=error" >> $GITHUB_ENV
|
||||
echo "MSG=Linux source verification cancelled" >> $GITHUB_ENV
|
||||
|
||||
- name: Set failure
|
||||
if: needs.source_verification.result == 'failure'
|
||||
run: |
|
||||
echo "RESULT=failure" >> $GITHUB_ENV
|
||||
echo "MSG=Linux source verification failed" >> $GITHUB_ENV
|
||||
|
||||
- name: Set final status
|
||||
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_SOURCE_VERIFICATION"
|
||||
|
|
|
|||
4
.github/workflows/labeler.yml
vendored
4
.github/workflows/labeler.yml
vendored
|
|
@ -8,12 +8,12 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Update labels based on changed files
|
||||
uses: actions/labeler@v4
|
||||
uses: actions/labeler@f27b608878404679385c85cfa523b85ccb86e213 # v6.1.0
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
- name: Update labels based on PR title
|
||||
id: labeler
|
||||
uses: fuxingloh/multi-labeler@f5bd7323b53b0833c1e4ed8d7b797ae995ef75b4 # v2.0.1
|
||||
uses: fuxingloh/multi-labeler@bcd50af464202999e57f556b4aefcf05a34abf85 # v5.0.0
|
||||
with:
|
||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||
config-path: .github/multi-labeler.yml
|
||||
|
|
|
|||
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.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"
|
||||
141
.github/workflows/pr-build-status.yml
vendored
141
.github/workflows/pr-build-status.yml
vendored
|
|
@ -1,40 +1,40 @@
|
|||
# GENERATED FILE - DO NOT EDIT!
|
||||
#
|
||||
# Keyman is copyright (C) SIL Global. MIT License.
|
||||
#
|
||||
# Do not modify the script in .github/workflows/pr-build-status.yml directly;
|
||||
# instead work on the sources in resources/build/pr-build-status and use the
|
||||
# build.sh script to rebuild the .github/workflows/pr-build-status.yml file from
|
||||
# them.
|
||||
#
|
||||
# build.sh will append the relevant portions of
|
||||
# resources/build/pr-build-status/pr-build-status.mjs to the content in
|
||||
# resources/build/pr-build-status/pr-build-status.prefix.yml to form the .github
|
||||
# workflow file.
|
||||
#
|
||||
name: Keyman Build Summary
|
||||
on:
|
||||
status:
|
||||
push:
|
||||
branches-ignore:
|
||||
- master
|
||||
- beta
|
||||
- stable-*
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
commit:
|
||||
description: 'Commit sha'
|
||||
required: true
|
||||
type: string
|
||||
jobs:
|
||||
run_pr_build_status:
|
||||
name: Summarize build status checks
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check PR build status
|
||||
id: run_pr_build_status_script
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
#
|
||||
# Keyman is copyright (C) SIL Global. MIT License.
|
||||
#
|
||||
# Do not modify the script in .github/workflows/pr-build-status.yml directly;
|
||||
# instead work on the sources in resources/build/pr-build-status and use the
|
||||
# build.sh script to rebuild the .github/workflows/pr-build-status.yml file from
|
||||
# them.
|
||||
#
|
||||
# build.sh will append the relevant portions of
|
||||
# resources/build/pr-build-status/pr-build-status.mjs to the content in
|
||||
# resources/build/pr-build-status/pr-build-status.prefix.yml to form the .github
|
||||
# workflow file.
|
||||
#
|
||||
name: Keyman Build Summary
|
||||
on:
|
||||
status:
|
||||
push:
|
||||
branches-ignore:
|
||||
- master
|
||||
- beta
|
||||
- stable-*
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
commit:
|
||||
description: 'Commit sha'
|
||||
required: true
|
||||
type: string
|
||||
jobs:
|
||||
run_pr_build_status:
|
||||
name: Summarize build status checks
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check PR build status
|
||||
id: run_pr_build_status_script
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
with:
|
||||
script: |
|
||||
// This code is copied out of resources/build/pr-build-status/pr-build-status.mjs
|
||||
// where it is tested. It is copied inline here in order to avoid requiring the
|
||||
|
|
@ -55,43 +55,70 @@ 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 == 'Linux source verification') {
|
||||
// Ignore Linux source verification -- we won't block automerge
|
||||
// for this at this point
|
||||
summary += addLog(`Skipping ${status.context}`);
|
||||
} else if(status.context == 'npm pack/publish') {
|
||||
summary += addStatus(o, 'build', status.context, status.state);
|
||||
} else if(status.context == 'Keyman Core - ARM64 test') {
|
||||
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
|
||||
// Note: function reduceStatuses() is where new status checks can be added
|
||||
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 +129,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 +146,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 +168,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 +182,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 +206,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 +218,7 @@ jobs:
|
|||
conclusion,
|
||||
output: {
|
||||
title: description,
|
||||
summary: ''
|
||||
summary
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -204,5 +233,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]);
|
||||
|
||||
|
|
|
|||
10
.gitignore
vendored
10
.gitignore
vendored
|
|
@ -103,8 +103,8 @@
|
|||
/windows/src/engine/keyman32/*.cod
|
||||
/windows/src/engine/keyman32/*.pdb
|
||||
|
||||
# /engine/keymanx64/
|
||||
/windows/src/engine/keymanx64/x64
|
||||
# /engine/keymanhp/
|
||||
/windows/src/engine/keymanhp/x64
|
||||
|
||||
# /engine/kmcomapi/
|
||||
/windows/src/engine/kmcomapi/kmcomapi.tlb
|
||||
|
|
@ -152,8 +152,8 @@
|
|||
# Atom/Nuclide
|
||||
compile_flags.txt
|
||||
|
||||
# Auto-generated resource script
|
||||
resources/environment.sh
|
||||
# Auto-generated resource script for xcode
|
||||
resources/build/mac/xcode-environment.inc.sh
|
||||
|
||||
# Copy of the master VERSION.md used for Linux packaging
|
||||
/core/VERSION.md
|
||||
|
|
@ -164,7 +164,7 @@ tsconfig.tsbuildinfo
|
|||
# Windows system files
|
||||
Thumbs.db
|
||||
|
||||
# Temporary file for logging scripts in xcode runs, see build-utils.sh for
|
||||
# Temporary file for logging scripts in xcode runs, see builder-basic.inc.sh for
|
||||
# details
|
||||
/xcodebuild-scripts.log
|
||||
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
|
|
|
|||
1113
HISTORY.md
1113
HISTORY.md
File diff suppressed because it is too large
Load diff
|
|
@ -1 +1 @@
|
|||
19.0.83
|
||||
19.0.251
|
||||
|
|
@ -3,19 +3,12 @@ buildscript {
|
|||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
flatDir {
|
||||
dirs "kMAPro/libs/com/stepstone/stepper/material-stepper/4.3.1/"
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.9.2'
|
||||
// sentry-android-gradle-plugin 2.1.5+ requires AGP 7.0
|
||||
classpath 'io.sentry:sentry-android-gradle-plugin:4.6.0'
|
||||
classpath 'io.sentry:sentry-android-gradle-plugin:5.9.0'
|
||||
classpath 'name.remal:gradle-plugins:1.9.2'
|
||||
|
||||
// From jcenter() which was deprecated August 2024
|
||||
// https://jfrog.com/blog/into-the-sunset-bintray-jcenter-gocenter-and-chartcenter/
|
||||
classpath 'com.stepstone.stepper:material-stepper:4.3.1@aar'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,11 +3,10 @@
|
|||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "${THIS_SCRIPT%/*}/../../resources/build/builder.inc.sh"
|
||||
. "${THIS_SCRIPT%/*}/../../resources/build/builder-full.inc.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
|
||||
. "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh"
|
||||
. "$KEYMAN_ROOT/resources/build/utils.inc.sh"
|
||||
. "$KEYMAN_ROOT/resources/build/build-help.inc.sh"
|
||||
. "$KEYMAN_ROOT/resources/build/build-download-resources.sh"
|
||||
|
||||
|
|
@ -28,8 +27,8 @@ builder_describe "Builds Keyman for Android app." \
|
|||
"configure" \
|
||||
"build" \
|
||||
"test Runs lint and unit tests." \
|
||||
"publish Publishes symbols to Sentry and the APK to the Play Store." \
|
||||
"--upload-sentry Upload to sentry"
|
||||
"publish-symbols Publishes symbols to Sentry." \
|
||||
"publish-play-store Publishes the APK to the Play Store."
|
||||
|
||||
# parse before describe_outputs to check debug flags
|
||||
builder_parse "$@"
|
||||
|
|
@ -41,9 +40,13 @@ if builder_is_debug_build; then
|
|||
TEST_FLAGS="-x assembleDebug lintDebug testDebug"
|
||||
fi
|
||||
|
||||
KEYBOARD_PACKAGE_ID="sil_euro_latin"
|
||||
KEYBOARDS_TARGET="android/KMAPro/kMAPro/src/main/assets/${KEYBOARD_PACKAGE_ID}.kmp"
|
||||
MODEL_PACKAGE_ID="nrc.en.mtnt"
|
||||
MODELS_TARGET="android/KMAPro/kMAPro/src/main/assets/${MODEL_PACKAGE_ID}.model.kmp"
|
||||
builder_describe_outputs \
|
||||
configure /android/KMAPro/kMAPro/libs/keyman-engine.aar \
|
||||
build /android/KMAPro/kMAPro/build/outputs/apk/$CONFIG/keyman-${KEYMAN_VERSION}.apk
|
||||
configure "/${MODELS_TARGET}" \
|
||||
build /android/KMAPro/kMAPro/build/outputs/apk/$CONFIG/keyman-${KEYMAN_VERSION_FOR_FILENAME}.apk
|
||||
|
||||
#### Build
|
||||
|
||||
|
|
@ -54,18 +57,6 @@ if builder_is_ci_build; then
|
|||
DAEMON_FLAG=--no-daemon
|
||||
fi
|
||||
|
||||
#### Build action definitions ####
|
||||
|
||||
function makeLocalSentryRelease() {
|
||||
echo "Making a Sentry release for tag $KEYMAN_VERSION_GIT_TAG"
|
||||
sentry-cli upload-dif -p keyman-android --include-sources
|
||||
sentry-cli releases -p keyman-android files $KEYMAN_VERSION_GIT_TAG upload-sourcemaps ./
|
||||
|
||||
echo "Finalizing release tag $KEYMAN_VERSION_GIT_TAG"
|
||||
sentry-cli releases finalize "$KEYMAN_VERSION_GIT_TAG"
|
||||
}
|
||||
|
||||
|
||||
#### Build action definitions ####
|
||||
|
||||
# Check about cleaning artifact paths
|
||||
|
|
@ -76,13 +67,8 @@ fi
|
|||
|
||||
if builder_start_action configure; then
|
||||
|
||||
KEYBOARD_PACKAGE_ID="sil_euro_latin"
|
||||
KEYBOARDS_TARGET="$KEYMAN_ROOT/android/KMAPro/kMAPro/src/main/assets/${KEYBOARD_PACKAGE_ID}.kmp"
|
||||
MODEL_PACKAGE_ID="nrc.en.mtnt"
|
||||
MODELS_TARGET="$KEYMAN_ROOT/android/KMAPro/kMAPro/src/main/assets/${MODEL_PACKAGE_ID}.model.kmp"
|
||||
|
||||
downloadKeyboardPackage "$KEYBOARD_PACKAGE_ID" "$KEYBOARDS_TARGET"
|
||||
downloadModelPackage "$MODEL_PACKAGE_ID" "$MODELS_TARGET"
|
||||
downloadKeyboardPackage "$KEYBOARD_PACKAGE_ID" "${KEYMAN_ROOT}/$KEYBOARDS_TARGET"
|
||||
downloadModelPackage "$MODEL_PACKAGE_ID" "${KEYMAN_ROOT}/$MODELS_TARGET"
|
||||
|
||||
builder_finish_action success configure
|
||||
fi
|
||||
|
|
@ -90,7 +76,7 @@ fi
|
|||
if builder_start_action build; then
|
||||
|
||||
# Copy Keyman Engine for Android
|
||||
cp "$KEYMAN_ROOT/android/KMEA/app/build/outputs/aar/keyman-engine-${CONFIG}.aar" "$KEYMAN_ROOT/android/KMAPro/kMAPro/libs/keyman-engine.aar"
|
||||
cp "${KEYMAN_ROOT}/android/KMEA/app/build/outputs/aar/keyman-engine-${CONFIG}.aar" "${KEYMAN_ROOT}/android/KMAPro/kMAPro/libs/keyman-engine.aar"
|
||||
|
||||
# Convert markdown to html for offline help
|
||||
build_help_html android KMAPro/kMAPro/src/main/assets/info
|
||||
|
|
@ -98,9 +84,7 @@ if builder_start_action build; then
|
|||
echo "BUILD_FLAGS $BUILD_FLAGS"
|
||||
./gradlew $DAEMON_FLAG clean $BUILD_FLAGS
|
||||
|
||||
if builder_has_option --upload-sentry; then
|
||||
makeLocalSentryRelease
|
||||
fi
|
||||
mv "${KEYMAN_ROOT}/android/KMAPro/kMAPro/build/outputs/apk/$CONFIG/keyman-${KEYMAN_VERSION}.apk" "${KEYMAN_ROOT}/android/KMAPro/kMAPro/build/outputs/apk/$CONFIG/keyman-${KEYMAN_VERSION_FOR_FILENAME}.apk"
|
||||
|
||||
builder_finish_action success build
|
||||
fi
|
||||
|
|
@ -113,12 +97,23 @@ if builder_start_action test; then
|
|||
builder_finish_action success test
|
||||
fi
|
||||
|
||||
if builder_start_action publish; then
|
||||
# Copy Release Notes
|
||||
generateReleaseNotes
|
||||
publish_symbols() {
|
||||
if builder_is_ci_build && builder_is_ci_build_level_release; then
|
||||
# TODO: what does publishSentry even do?
|
||||
./gradlew $DAEMON_FLAG publishSentry
|
||||
builder_echo "Making a Sentry release for tag $KEYMAN_VERSION_GIT_TAG"
|
||||
sentry-cli upload-dif -p keyman-android --include-sources
|
||||
sentry-cli releases -p keyman-android files $KEYMAN_VERSION_GIT_TAG upload-sourcemaps ./
|
||||
fi
|
||||
}
|
||||
|
||||
# Publish symbols and Keyman for Android to Play Store
|
||||
./gradlew $DAEMON_FLAG publishSentry publishReleaseApk
|
||||
publish_play_store() {
|
||||
if builder_is_ci_build && builder_is_ci_build_level_release; then
|
||||
generateReleaseNotes
|
||||
# Publish Keyman for Android to Play Store
|
||||
./gradlew $DAEMON_FLAG publishReleaseApk
|
||||
fi
|
||||
}
|
||||
|
||||
builder_finish_action success publish
|
||||
fi
|
||||
builder_run_action publish-symbols publish_symbols
|
||||
builder_run_action publish-play-store publish_play_store
|
||||
|
|
|
|||
|
|
@ -11,8 +11,14 @@ apply from: "$rootPath/version.gradle"
|
|||
|
||||
String tier = new File('../../TIER.md').getText('UTF-8').trim();
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(21)
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk 34
|
||||
compileSdk 35
|
||||
namespace="com.tavultesoft.kmapro"
|
||||
|
||||
// Don't compress kmp files so they can be copied via AssetManager
|
||||
|
|
@ -24,15 +30,10 @@ android {
|
|||
buildConfig = true
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.tavultesoft.kmapro"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 34
|
||||
targetSdkVersion 35
|
||||
|
||||
//println "===DUMPING PROPERTIES==="
|
||||
//dumpProperties(project) // Use this to dump all external properties for debugging TeamCity integration
|
||||
|
|
@ -113,6 +114,12 @@ android {
|
|||
disable 'MissingQuantity', 'MissingTranslation'
|
||||
lintConfig file("lint.xml")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
jniLibs.srcDirs = ['libs']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// how to configure the sentry android gradle plugin
|
||||
|
|
@ -157,21 +164,20 @@ if (env_keys_json_file != null && file(env_keys_json_file).exists()) {
|
|||
}
|
||||
|
||||
repositories {
|
||||
flatDir {
|
||||
dirs 'libs'
|
||||
}
|
||||
google()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
|
||||
implementation 'com.google.android.material:material:1.12.0'
|
||||
implementation 'com.stepstone.stepper:material-stepper:4.3.1'
|
||||
api(name: 'keyman-engine', ext: 'aar')
|
||||
implementation 'io.sentry:sentry-android:7.8.0'
|
||||
implementation 'androidx.webkit:webkit:1.14.0'
|
||||
implementation files('libs/keyman-engine.aar')
|
||||
implementation 'io.sentry:sentry-android:8.19.1'
|
||||
implementation 'androidx.preference:preference:1.2.1'
|
||||
implementation "com.android.installreferrer:installreferrer:2.2"
|
||||
implementation 'com.android.installreferrer:installreferrer:2.2'
|
||||
|
||||
// Add dependency for generating QR Codes
|
||||
// (Even though it's embedded in KMEA, because we're manually copying keyman-engine.aar,
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@
|
|||
package com.keyman.android;
|
||||
|
||||
import com.keyman.engine.util.DownloadFileUtils;
|
||||
import com.tavultesoft.kmapro.AdjustLongpressDelayActivity;
|
||||
import com.tavultesoft.kmapro.BuildConfig;
|
||||
import com.tavultesoft.kmapro.DefaultLanguageResource;
|
||||
import com.tavultesoft.kmapro.KeymanSettingsActivity;
|
||||
import com.tavultesoft.kmapro.PreferencesManager;
|
||||
import com.keyman.engine.KMManager;
|
||||
import com.keyman.engine.KMManager.KeyboardType;
|
||||
import com.keyman.engine.KMHardwareKeyboardInterpreter;
|
||||
|
|
@ -30,12 +30,12 @@ import android.view.KeyEvent;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.ExtractedText;
|
||||
import android.view.inputmethod.ExtractedTextRequest;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.widget.FrameLayout;
|
||||
import android.content.Intent;
|
||||
|
||||
import io.sentry.android.core.SentryAndroid;
|
||||
import io.sentry.Sentry;
|
||||
|
|
@ -43,7 +43,6 @@ import io.sentry.Sentry;
|
|||
public class SystemKeyboard extends InputMethodService implements OnKeyboardEventListener {
|
||||
|
||||
private static View inputView = null;
|
||||
private static ExtractedText exText = null;
|
||||
private KMHardwareKeyboardInterpreter interpreter = null;
|
||||
private int inputType = InputType.TYPE_NULL;
|
||||
private int lastOrientation = Configuration.ORIENTATION_UNDEFINED;
|
||||
|
|
@ -125,8 +124,9 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
|
|||
}
|
||||
|
||||
ViewGroup parent = (ViewGroup) inputView.getParent();
|
||||
if (parent != null)
|
||||
if (parent != null) {
|
||||
parent.removeView(inputView);
|
||||
}
|
||||
|
||||
return inputView;
|
||||
}
|
||||
|
|
@ -140,6 +140,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
|
||||
|
|
@ -187,10 +203,9 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
|
|||
return value, so we test for that as well (#11479)
|
||||
*/
|
||||
if (icText != null && icText.text != null) {
|
||||
boolean didUpdateText = KMManager.updateText(KeyboardType.KEYBOARD_TYPE_SYSTEM, icText.text.toString());
|
||||
boolean didUpdateSelection = KMManager.updateSelectionRange(KeyboardType.KEYBOARD_TYPE_SYSTEM);
|
||||
if (!didUpdateText || !didUpdateSelection)
|
||||
exText = icText;
|
||||
// Update the text selection but ignore the returned statuses
|
||||
KMManager.updateText(KeyboardType.KEYBOARD_TYPE_SYSTEM, icText.text.toString());
|
||||
KMManager.updateSelectionRange(KeyboardType.KEYBOARD_TYPE_SYSTEM);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -198,6 +213,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
|
||||
|
|
@ -238,7 +257,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;
|
||||
|
|
@ -246,9 +265,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);
|
||||
|
|
@ -256,15 +276,15 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
|
|||
|
||||
@Override
|
||||
public void onKeyboardLoaded(KeyboardType keyboardType) {
|
||||
if (keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
|
||||
if (exText != null)
|
||||
exText = null;
|
||||
}
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onKeyboardChanged(String newKeyboard) {
|
||||
// Refresh banner theme
|
||||
BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_SYSTEM);
|
||||
KMManager.showSystemKeyboard();
|
||||
sendCurrentFontName();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -278,6 +298,21 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
|
|||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onEvaluateInputViewShown() {
|
||||
// On Android API 36+, the OSK defaults to not appearing when a physical keyboard is connected.
|
||||
// If the default implementation returns true, recommend honoring it
|
||||
// Reference: https://android.googlesource.com/platform/frameworks/base/+/7b739a8%5E%21/
|
||||
if (super.onEvaluateInputViewShown()) {
|
||||
return true;
|
||||
};
|
||||
|
||||
Context context = getApplicationContext();
|
||||
SharedPreferences prefs = context.getSharedPreferences(PreferencesManager.kma_prefs_name, Context.MODE_PRIVATE);
|
||||
boolean showOSK = prefs.getBoolean(KeymanSettingsActivity.oskWithPhysicalKeyboardKey, false);
|
||||
return showOSK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
// Determine if Physical keystroke should be passed off
|
||||
|
|
|
|||
|
|
@ -22,29 +22,28 @@ import com.keyman.engine.BaseActivity;
|
|||
import com.keyman.engine.KMManager;
|
||||
|
||||
/**
|
||||
* Settings menu for adjusting the keyboard height. The value for the current device orientation
|
||||
* is saved in shared preferences.
|
||||
* Settings menu for adjusting the keyboard height.
|
||||
* The value for the current device orientation is saved via KMManager.
|
||||
*/
|
||||
public class AdjustKeyboardHeightActivity extends BaseActivity {
|
||||
private static final String TAG = "AdjustKbdHeight";
|
||||
public static final String adjustKeyboardHeightKey = "AdjustKeyboardHeight";
|
||||
|
||||
private static Button resetButton = null;
|
||||
private static ImageView sampleKeyboard = null;
|
||||
|
||||
// Keeps track of the adjusted keyboard height for saving
|
||||
private static int currentHeight = 0;
|
||||
private static ViewGroup.LayoutParams layoutParams;
|
||||
private Button resetButton;
|
||||
private ImageView sampleKeyboard;
|
||||
private TextView percentageDisplay;
|
||||
private int currentHeight;
|
||||
private ViewGroup.LayoutParams layoutParams;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
final Context context = this;
|
||||
|
||||
setContentView(R.layout.activity_adjust_keyboard_height);
|
||||
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.titlebar);
|
||||
setupEdgeToEdge(R.id.adjust_keyboard_layout);
|
||||
setupStatusBarColors(R.color.keyman_blue, android.R.color.white);
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.titlebar);
|
||||
setSupportActionBar(toolbar);
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
|
|
@ -57,75 +56,162 @@ public class AdjustKeyboardHeightActivity extends BaseActivity {
|
|||
actionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.keyman_blue)));
|
||||
}
|
||||
|
||||
TextView adjustKeyboardHeightActivityTitle = (TextView) findViewById(R.id.bar_title);
|
||||
|
||||
String titleStr = getString(R.string.adjust_keyboard_height);
|
||||
TextView adjustKeyboardHeightActivityTitle = findViewById(R.id.bar_title);
|
||||
adjustKeyboardHeightActivityTitle.setTextColor(ContextCompat.getColor(this, R.color.ms_white));
|
||||
adjustKeyboardHeightActivityTitle.setText(titleStr);
|
||||
adjustKeyboardHeightActivityTitle.setText(getString(R.string.adjust_keyboard_height));
|
||||
|
||||
sampleKeyboard = findViewById(R.id.sample_keyboard);
|
||||
percentageDisplay = findViewById(R.id.keyboard_height_percentage);
|
||||
View resizeHandle = findViewById(R.id.resize_handle);
|
||||
|
||||
sampleKeyboard = (ImageView) findViewById(R.id.sample_keyboard);
|
||||
layoutParams = sampleKeyboard.getLayoutParams();
|
||||
currentHeight = KMManager.getKeyboardHeight(context);
|
||||
refreshSampleKeyboard(context);
|
||||
currentHeight = KMManager.getKeyboardHeight(this);
|
||||
refreshSampleKeyboard();
|
||||
|
||||
resetButton = (Button) findViewById(R.id.reset_to_defaults);
|
||||
resetButton.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
// Clear the keyboard height preferences to reset
|
||||
KMManager.applyKeyboardHeight(context, KMManager.KeyboardHeight_Reset);
|
||||
|
||||
// Restore default height
|
||||
currentHeight = KMManager.getKeyboardHeight(context);
|
||||
refreshSampleKeyboard(context);
|
||||
}
|
||||
resetButton = findViewById(R.id.reset_to_defaults);
|
||||
resetButton.setOnClickListener(v -> {
|
||||
KMManager.applyKeyboardHeight(this, KMManager.KeyboardHeight_Reset);
|
||||
currentHeight = KMManager.getKeyboardHeight(this);
|
||||
refreshSampleKeyboard();
|
||||
});
|
||||
|
||||
sampleKeyboard.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent event) {
|
||||
int y = (int)event.getY();
|
||||
|
||||
switch(event.getAction()) {
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
// Update currentHeight as the user drags (moves)
|
||||
// Increasing the keyboard height is a negative y
|
||||
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);
|
||||
|
||||
refreshSampleKeyboard(context);
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
// Save the currentHeight when the user releases
|
||||
KMManager.applyKeyboardHeight(context, currentHeight);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
View.OnTouchListener touchListener = (view, event) -> {
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
view.setPressed(true);
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
int[] location = new int[2];
|
||||
sampleKeyboard.getLocationOnScreen(location);
|
||||
int viewBottom = location[1] + sampleKeyboard.getHeight();
|
||||
int touchY = (int) event.getRawY();
|
||||
currentHeight = calculateKeyboardHeightFromTouch(touchY, viewBottom);
|
||||
refreshSampleKeyboard();
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
view.setPressed(false);
|
||||
KMManager.applyKeyboardHeight(this, currentHeight);
|
||||
break;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
};
|
||||
|
||||
resizeHandle.setOnTouchListener(touchListener);
|
||||
sampleKeyboard.setOnTouchListener(touchListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the layout for the sample keyboard
|
||||
* @param context
|
||||
* Refresh the layout for the sample keyboard and update percentage display.
|
||||
*/
|
||||
private void refreshSampleKeyboard(Context context) {
|
||||
layoutParams.height = currentHeight;
|
||||
sampleKeyboard.setLayoutParams(layoutParams);
|
||||
private void refreshSampleKeyboard() {
|
||||
if (layoutParams != null && sampleKeyboard != null) {
|
||||
layoutParams.height = currentHeight;
|
||||
sampleKeyboard.requestLayout();
|
||||
|
||||
if (percentageDisplay != null) {
|
||||
percentageDisplay.setText(createLiveKeyboardHeightString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate percentage from height and default height.
|
||||
* @param height Current height
|
||||
* @param defaultHeight Default height
|
||||
* @return Percentage rounded to nearest integer
|
||||
*/
|
||||
private static int calculatePercentage(int height, int defaultHeight) {
|
||||
if (defaultHeight == 0 || height == 0) {
|
||||
return 100;
|
||||
}
|
||||
return (int) Math.round((double) height / defaultHeight * 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a string showing the keyboard height as percentages for both orientations.
|
||||
* @param context Context
|
||||
* @return String in format "100% Portrait | 100% Landscape"
|
||||
*/
|
||||
public static String createKeyboardHeightString(Context context) {
|
||||
int portraitHeight = KMManager.getKeyboardHeight(context, Configuration.ORIENTATION_PORTRAIT);
|
||||
int landscapeHeight = KMManager.getKeyboardHeight(context, Configuration.ORIENTATION_LANDSCAPE);
|
||||
|
||||
int portraitDefault = KMManager.getDefaultKeyboardHeight(Configuration.ORIENTATION_PORTRAIT);
|
||||
int landscapeDefault = KMManager.getDefaultKeyboardHeight(Configuration.ORIENTATION_LANDSCAPE);
|
||||
|
||||
int portraitPercent = calculatePercentage(portraitHeight, portraitDefault);
|
||||
int landscapePercent = calculatePercentage(landscapeHeight, landscapeDefault);
|
||||
|
||||
return context.getString(R.string.keyboard_height_format, portraitPercent, landscapePercent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate keyboard height from touch Y coordinate and clamp
|
||||
* within min-max bounds.
|
||||
* @param touchY Touch Y coordinate in screen coordinates
|
||||
* @param viewBottom Bottom Y coordinate of keyboard view
|
||||
* @return Keyboard height in pixels, clamped to min/max bounds
|
||||
*/
|
||||
private int calculateKeyboardHeightFromTouch(int touchY, int viewBottom) {
|
||||
int height = viewBottom - touchY;
|
||||
int minKeyboardHeight = KMManager.getKeyboardHeightMin(this);
|
||||
int maxKeyboardHeight = KMManager.getKeyboardHeightMax(this);
|
||||
return Math.max(minKeyboardHeight, Math.min(maxKeyboardHeight, height));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a string showing keyboard height percentages for both orientations,
|
||||
* using the current (unsaved) height for the active orientation.
|
||||
* @return String in format "100% Portrait | 100% Landscape"
|
||||
*/
|
||||
private String createLiveKeyboardHeightString() {
|
||||
int currentOrientation = KMManager.getOrientation(this);
|
||||
|
||||
int portraitDefault = KMManager.getDefaultKeyboardHeight(Configuration.ORIENTATION_PORTRAIT);
|
||||
int landscapeDefault = KMManager.getDefaultKeyboardHeight(Configuration.ORIENTATION_LANDSCAPE);
|
||||
|
||||
int portraitHeight;
|
||||
int landscapeHeight;
|
||||
if (currentOrientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||
portraitHeight = currentHeight;
|
||||
landscapeHeight = KMManager.getKeyboardHeight(this, Configuration.ORIENTATION_LANDSCAPE);
|
||||
} else {
|
||||
landscapeHeight = currentHeight;
|
||||
portraitHeight = KMManager.getKeyboardHeight(this, Configuration.ORIENTATION_PORTRAIT);
|
||||
}
|
||||
|
||||
int portraitPercent = calculatePercentage(portraitHeight, portraitDefault);
|
||||
int landscapePercent = calculatePercentage(landscapeHeight, landscapeDefault);
|
||||
|
||||
return getString(R.string.keyboard_height_format, portraitPercent, landscapePercent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
// Ensure height is saved even if user switches tasks or navigates away abnormally
|
||||
// This prevents the touch zone from being out of sync with the keyboard visual
|
||||
if (currentHeight > 0) {
|
||||
KMManager.applyKeyboardHeight(this, currentHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
|
||||
// IMPORTANT: Apply current height BEFORE switching orientations
|
||||
// This ensures the old orientation's height is saved before loading the new one
|
||||
if (currentHeight > 0) {
|
||||
KMManager.applyKeyboardHeight(this, currentHeight);
|
||||
}
|
||||
|
||||
layoutParams = sampleKeyboard.getLayoutParams();
|
||||
|
||||
// When the user rotates the device, restore currentHeight
|
||||
// When the user rotates the device, restore currentHeight for the new orientation
|
||||
currentHeight = KMManager.getKeyboardHeight(this);
|
||||
refreshSampleKeyboard(this);
|
||||
refreshSampleKeyboard();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ public class InfoActivity extends BaseActivity {
|
|||
final Context context = this;
|
||||
|
||||
setContentView(R.layout.activity_info);
|
||||
setupEdgeToEdge(R.id.info_layout);
|
||||
|
||||
// Navigation buttons
|
||||
final ImageButton backButton = (ImageButton) findViewById(R.id.back_button);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import android.graphics.Bitmap;
|
|||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.LocaleList;
|
||||
import android.util.AndroidRuntimeException;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
|
@ -38,7 +39,7 @@ public class KMPBrowserActivity extends BaseActivity {
|
|||
private static final String TAG = "KMPBrowserActivity";
|
||||
|
||||
// URL for keyboard search web page presented to user when they add a keyboard in the app.
|
||||
private static final String KMP_SEARCH_KEYBOARDS_FORMATSTR = "https://%s/go/android/%s/download-keyboards%s";
|
||||
private static final String KMP_SEARCH_KEYBOARDS_FORMATSTR = "https://%s/go/android/%s/download-keyboards%s?lang=%s";
|
||||
private static final String KMP_SEARCH_KEYBOARDS_LANGUAGES = "/languages/%s";
|
||||
|
||||
// Patterns for determining if a link should be opened in external browser
|
||||
|
|
@ -79,6 +80,9 @@ public class KMPBrowserActivity extends BaseActivity {
|
|||
return;
|
||||
}
|
||||
|
||||
setupEdgeToEdge(R.id.kmp_browser_layout);
|
||||
setupStatusBarColors(R.color.complementary_5, android.R.color.white);
|
||||
|
||||
webView = (WebView) findViewById(R.id.kmpBrowserWebView);
|
||||
webView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
|
||||
webView.getSettings().setJavaScriptEnabled(true);
|
||||
|
|
@ -160,13 +164,7 @@ public class KMPBrowserActivity extends BaseActivity {
|
|||
}
|
||||
});
|
||||
|
||||
// Tier determines the keyboard search host
|
||||
String host = KMPLink.getHost();
|
||||
// If language ID is provided, include it in the keyboard search
|
||||
String languageID = getIntent().getStringExtra("languageCode");
|
||||
String languageStr = (languageID != null) ? KMString.format(KMP_SEARCH_KEYBOARDS_LANGUAGES, languageID) : "";
|
||||
String appMajorVersion = KMManager.getMajorVersion();
|
||||
String kmpSearchUrl = KMString.format(KMP_SEARCH_KEYBOARDS_FORMATSTR, host, appMajorVersion, languageStr);
|
||||
String kmpSearchUrl = determineSearchUrl();
|
||||
webView.loadUrl(kmpSearchUrl);
|
||||
}
|
||||
|
||||
|
|
@ -175,7 +173,8 @@ public class KMPBrowserActivity extends BaseActivity {
|
|||
super.onResume();
|
||||
|
||||
if (webView != null) {
|
||||
webView.reload();
|
||||
String kmpSearchUrl = determineSearchUrl();
|
||||
webView.loadUrl(kmpSearchUrl);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -218,6 +217,32 @@ public class KMPBrowserActivity extends BaseActivity {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the keyman.com keyboard search URL while accounting for:
|
||||
* host (production or staging)
|
||||
* languageID - language ID to search for
|
||||
* display language - request search results to be displayed in this language
|
||||
* @return String
|
||||
*/
|
||||
private String determineSearchUrl() {
|
||||
// Tier determines the keyboard search host
|
||||
String host = KMPLink.getHost();
|
||||
|
||||
// If display language is provided, include it in the keyboard search. Otherwise fallback to default locale
|
||||
String fallbackLang = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) ?
|
||||
LocaleList.getDefault().get(0).toLanguageTag() : "en";
|
||||
Bundle bundle = getIntent().getExtras();
|
||||
String displayLang = (bundle != null) && bundle.containsKey("lang") ? bundle.getString("lang") : fallbackLang;
|
||||
|
||||
// If language ID is provided for keyboard search, include it in the keyboard search
|
||||
String languageID = getIntent().getStringExtra("languageCode");
|
||||
String languageStr = (languageID != null) ? KMString.format(KMP_SEARCH_KEYBOARDS_LANGUAGES, languageID) : "";
|
||||
|
||||
String appMajorVersion = KMManager.getMajorVersion();
|
||||
String kmpSearchUrl = KMString.format(KMP_SEARCH_KEYBOARDS_FORMATSTR, host, appMajorVersion, languageStr, displayLang);
|
||||
return kmpSearchUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a URL is a valid internal Keyman keyboard link
|
||||
* @param url String of the URL to parse
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import android.os.Bundle;
|
|||
import com.keyman.engine.BaseActivity;
|
||||
import com.keyman.engine.KMManager;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
public class KeymanSettingsActivity extends BaseActivity {
|
||||
|
|
@ -14,6 +13,7 @@ public class KeymanSettingsActivity extends BaseActivity {
|
|||
protected static final String sendCrashReport = "SendCrashReport";
|
||||
public static final String spacebarTextKey = "SpacebarText";
|
||||
public static final String hapticFeedbackKey = "HapticFeedback";
|
||||
public static final String oskWithPhysicalKeyboardKey = "ShowOSK";
|
||||
|
||||
protected KeymanSettingsFragment innerFragment;
|
||||
|
||||
|
|
@ -22,6 +22,10 @@ public class KeymanSettingsActivity extends BaseActivity {
|
|||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.keyman_settings_layout);
|
||||
|
||||
setupEdgeToEdge(R.id.keyman_settings_layout);
|
||||
setupStatusBarColors(R.color.keyman_blue, android.R.color.white);
|
||||
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.keyman_settings_toolbar);
|
||||
toolbar.setTitleTextColor(getResources().getColor(android.R.color.white));
|
||||
toolbar.setTitle(R.string.keyman_settings);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ package com.tavultesoft.kmapro;
|
|||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
|
@ -20,6 +21,7 @@ import androidx.preference.SwitchPreference;
|
|||
import com.keyman.engine.DisplayLanguages;
|
||||
import com.keyman.engine.KMManager;
|
||||
import com.keyman.engine.data.Dataset;
|
||||
import com.tavultesoft.kmapro.PreferencesManager;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
|
|
@ -104,6 +106,7 @@ public class KeymanSettingsFragment extends PreferenceFragmentCompat {
|
|||
adjustKeyboardHeight.setKey(AdjustKeyboardHeightActivity.adjustKeyboardHeightKey);
|
||||
adjustKeyboardHeight.setTitle(getString(R.string.adjust_keyboard_height));
|
||||
adjustKeyboardHeight.setWidgetLayoutResource(R.layout.preference_height_icon_layout);
|
||||
adjustKeyboardHeight.setSummary(AdjustKeyboardHeightActivity.createKeyboardHeightString(context));
|
||||
Intent adjustKeyboardHeightIntent = new Intent(context, AdjustKeyboardHeightActivity.class);
|
||||
adjustKeyboardHeight.setIntent(adjustKeyboardHeightIntent);
|
||||
|
||||
|
|
@ -157,6 +160,31 @@ public class KeymanSettingsFragment extends PreferenceFragmentCompat {
|
|||
}
|
||||
});
|
||||
|
||||
SwitchPreference oskWithPhysicalKeyboardPreference = new SwitchPreference(context);
|
||||
oskWithPhysicalKeyboardPreference.setKey(KeymanSettingsActivity.oskWithPhysicalKeyboardKey);
|
||||
oskWithPhysicalKeyboardPreference.setTitle(getString(R.string.show_osk));
|
||||
oskWithPhysicalKeyboardPreference.setSummary(getString(R.string.show_osk_hint));
|
||||
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
PreferencesManager.kma_prefs_name, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
boolean showOSK = prefs.getBoolean(
|
||||
KeymanSettingsActivity.oskWithPhysicalKeyboardKey, false);
|
||||
oskWithPhysicalKeyboardPreference.setDefaultValue(showOSK);
|
||||
|
||||
oskWithPhysicalKeyboardPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
if (newValue == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
editor.putBoolean(KeymanSettingsActivity.oskWithPhysicalKeyboardKey, (boolean)newValue);
|
||||
editor.commit();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
SwitchPreference hapticFeedbackPreference = new SwitchPreference(context);
|
||||
hapticFeedbackPreference.setKey(KeymanSettingsActivity.hapticFeedbackKey);
|
||||
hapticFeedbackPreference.setTitle(getString(R.string.haptic_feedback));
|
||||
|
|
@ -198,6 +226,8 @@ public class KeymanSettingsFragment extends PreferenceFragmentCompat {
|
|||
|
||||
screen.addPreference(adjustKeyboardHeight);
|
||||
screen.addPreference(adjustLongpressDelay);
|
||||
|
||||
screen.addPreference(oskWithPhysicalKeyboardPreference);
|
||||
screen.addPreference(spacebarTextPreference);
|
||||
|
||||
screen.addPreference(hapticFeedbackPreference);
|
||||
|
|
@ -239,5 +269,7 @@ public class KeymanSettingsFragment extends PreferenceFragmentCompat {
|
|||
|
||||
// Update the language / keyboard count when we return to this menu from deeper levels.
|
||||
languagesPreference.setTitle(getInstalledLanguagesText());
|
||||
|
||||
adjustKeyboardHeight.setSummary(AdjustKeyboardHeightActivity.createKeyboardHeightString(context));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,11 +7,12 @@ import java.util.ArrayList;
|
|||
import java.util.HashMap;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Typeface;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
|
@ -24,11 +25,13 @@ import android.os.Bundle;
|
|||
import android.widget.SimpleAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.keyman.engine.BaseActivity;
|
||||
import com.keyman.engine.DisplayLanguages;
|
||||
import com.keyman.engine.KMManager;
|
||||
import com.keyman.engine.data.KeyboardController;
|
||||
import com.keyman.engine.util.MapCompat;
|
||||
|
||||
public class KeymanSettingsInstallActivity extends AppCompatActivity {
|
||||
public class KeymanSettingsInstallActivity extends BaseActivity {
|
||||
private static final String TAG = "SettingsInstallActivity";
|
||||
private static ArrayList<HashMap<String, String>> installOptionList = null;
|
||||
private static Typeface titleFont = null;
|
||||
|
|
@ -45,6 +48,8 @@ public class KeymanSettingsInstallActivity extends AppCompatActivity {
|
|||
context = this;
|
||||
|
||||
setContentView(R.layout.activity_list_layout);
|
||||
setupEdgeToEdge(R.id.list_toolbar);
|
||||
|
||||
final Toolbar toolbar = findViewById(R.id.list_toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
|
@ -125,12 +130,19 @@ public class KeymanSettingsInstallActivity extends AppCompatActivity {
|
|||
HashMap<String, String> hashMap = (HashMap<String, String>) parent.getItemAtPosition(position);
|
||||
String itemTitle = MapCompat.getOrDefault(hashMap, titleKey, "");
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
String languageTag = prefs.getString(DisplayLanguages.displayLanguageKey, "");
|
||||
// Install from keyman.com
|
||||
if (itemTitle.equals(getString(R.string.install_from_keyman_dot_com))) {
|
||||
if (KMManager.hasConnection(context)) {
|
||||
Intent i = new Intent(context, KMPBrowserActivity.class);
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
if (languageTag != null && !languageTag.isEmpty()) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("lang", languageTag);
|
||||
i.putExtras(bundle);
|
||||
}
|
||||
context.startActivity(i);
|
||||
} else {
|
||||
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);
|
||||
|
|
|
|||
|
|
@ -25,10 +25,12 @@ import android.widget.TextView;
|
|||
import androidx.annotation.IdRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.SwitchCompat;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import com.keyman.engine.BaseActivity;
|
||||
import com.keyman.engine.DisplayLanguages;
|
||||
import com.keyman.engine.KeyboardPickerActivity;
|
||||
import com.keyman.engine.KMManager;
|
||||
import com.keyman.engine.ModelPickerActivity;
|
||||
|
|
@ -43,7 +45,7 @@ import java.util.HashMap;
|
|||
* Keyman Settings --> Languages Settings --> Language Settings
|
||||
* Displays a list of installed keyboards and some lexical model switches.
|
||||
*/
|
||||
public final class LanguageSettingsActivity extends AppCompatActivity {
|
||||
public final class LanguageSettingsActivity extends BaseActivity {
|
||||
private Context context;
|
||||
private static Toolbar toolbar = null;
|
||||
private static ListView listView = null;
|
||||
|
|
@ -66,6 +68,8 @@ public final class LanguageSettingsActivity extends AppCompatActivity {
|
|||
context = this;
|
||||
setContentView(R.layout.language_settings_list_layout);
|
||||
|
||||
setupEdgeToEdge(R.id.list_toolbar);
|
||||
|
||||
toolbar = (Toolbar) findViewById(R.id.list_toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
|
@ -199,11 +203,19 @@ public final class LanguageSettingsActivity extends AppCompatActivity {
|
|||
if (KMManager.hasConnection(context)){
|
||||
// Scenario 1: Connection to keyman.com catalog
|
||||
// Pass the BCP47 language code to the KMPBrowserActivity
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
String languageTag = prefs.getString(DisplayLanguages.displayLanguageKey, "");
|
||||
|
||||
Intent i = new Intent(context, KMPBrowserActivity.class);
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
i.putExtra("languageCode", lgCode);
|
||||
i.putExtra("languageName", lgName);
|
||||
if (languageTag != null && !languageTag.isEmpty()) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("lang", languageTag);
|
||||
i.putExtras(bundle);
|
||||
}
|
||||
context.startActivity(i);
|
||||
/*
|
||||
} else if (KeyboardPickerActivity.hasKeyboardFromPackage()) {
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ import android.widget.TextView;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
import com.keyman.engine.BaseActivity;
|
||||
import com.keyman.engine.data.Dataset;
|
||||
import com.keyman.engine.data.adapters.AdapterFilter;
|
||||
import com.keyman.engine.data.adapters.NestedAdapter;
|
||||
|
|
@ -36,7 +36,7 @@ import java.util.List;
|
|||
* Keyman Settings --> Languages Settings
|
||||
* Displays a list of installed languages and a count of their associated installed keyboards.
|
||||
*/
|
||||
public final class LanguagesSettingsActivity extends AppCompatActivity {
|
||||
public final class LanguagesSettingsActivity extends BaseActivity {
|
||||
|
||||
private Context context;
|
||||
private static Toolbar toolbar = null;
|
||||
|
|
@ -53,6 +53,8 @@ public final class LanguagesSettingsActivity extends AppCompatActivity {
|
|||
context = this;
|
||||
setContentView(R.layout.languages_settings_list_layout);
|
||||
|
||||
setupEdgeToEdge(R.id.languages_list_layout);
|
||||
|
||||
toolbar = (Toolbar) findViewById(R.id.list_toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ import androidx.activity.result.ActivityResultLauncher;
|
|||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import android.content.ClipData;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
|
|
@ -85,7 +86,6 @@ import android.graphics.drawable.BitmapDrawable;
|
|||
import android.graphics.drawable.Drawable;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import android.provider.Settings;
|
||||
import android.text.Html;
|
||||
|
|
@ -98,8 +98,6 @@ import android.view.MenuItem;
|
|||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.webkit.WebView;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.SeekBar;
|
||||
|
|
@ -118,6 +116,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
|
|||
|
||||
private static final String TAG = "MainActivity";
|
||||
|
||||
private ConstraintLayout constraintLayout;
|
||||
private KMTextView textView;
|
||||
private final int minTextSize = 16;
|
||||
private final int maxTextSize = 72;
|
||||
|
|
@ -170,6 +169,10 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
|
|||
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
constraintLayout = (ConstraintLayout)findViewById(R.id.constraintLayout);
|
||||
setupEdgeToEdge(R.id.constraintLayout);
|
||||
setupStatusBarColors(android.R.color.white, R.color.neutral_2);
|
||||
|
||||
toolbar = (Toolbar) findViewById(R.id.titlebar);
|
||||
setSupportActionBar(toolbar);
|
||||
getSupportActionBar().setTitle(null);
|
||||
|
|
@ -616,16 +619,21 @@ 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);
|
||||
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);
|
||||
textView.setHeight(
|
||||
screenHeight - statusBarHeight - actionBarHeight - bannerHeight - keyboardHeight - navigationBarHeight);
|
||||
}
|
||||
|
||||
private void showInfo() {
|
||||
|
|
@ -864,6 +872,9 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
|
|||
|
||||
private void displayUpdateChrome() {
|
||||
// TextView's default string is to update Chrome
|
||||
TextView textView = (TextView)findViewById(R.id.kmWebViewChromeTextView);
|
||||
textView.setText(String.format(getString(R.string.text_require_chrome_version),
|
||||
WebViewUtils.KEYMAN_MIN_TARGET_VERSION_ANDROID_CHROME));
|
||||
|
||||
Button button = (Button)findViewById(R.id.webViewChromeButton);
|
||||
button.setText(getString(R.string.button_update_chrome));
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
package com.tavultesoft.kmapro;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
|
|
@ -18,6 +17,7 @@ import android.widget.Toast;
|
|||
|
||||
import com.stepstone.stepper.StepperLayout;
|
||||
import com.stepstone.stepper.VerificationError;
|
||||
import com.keyman.engine.BaseActivity;
|
||||
import com.keyman.engine.KMManager;
|
||||
import com.keyman.engine.KeyboardEventHandler;
|
||||
import com.keyman.engine.KmpInstallMode;
|
||||
|
|
@ -34,7 +34,7 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class PackageActivity extends AppCompatActivity implements
|
||||
public class PackageActivity extends BaseActivity implements
|
||||
StepperLayout.StepperListener, WebViewFragment.OnInstallClickedListener,
|
||||
SelectLanguageFragment.OnLanguagesSelectedListener {
|
||||
private final static String TAG = "PackageActivity";
|
||||
|
|
@ -52,6 +52,7 @@ public class PackageActivity extends AppCompatActivity implements
|
|||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_package_installer);
|
||||
setupEdgeToEdge(R.id.package_installer_layout);
|
||||
|
||||
KmpInstallMode installMode = KmpInstallMode.Full;
|
||||
String languageID = null;
|
||||
|
|
@ -84,7 +85,8 @@ public class PackageActivity extends AppCompatActivity 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);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
/**
|
||||
* Keyman is copyright (C) SIL Global. MIT License.
|
||||
*
|
||||
*/
|
||||
package com.tavultesoft.kmapro;
|
||||
|
||||
public class PreferencesManager {
|
||||
|
||||
// TODO: Separate this from the Keyman Engine preference key name in strings.xml: #13633
|
||||
public static final String kma_prefs_name = "KMAPreferences";
|
||||
}
|
||||
|
|
@ -8,11 +8,11 @@ import android.os.Bundle;
|
|||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.stepstone.stepper.StepperLayout;
|
||||
import com.stepstone.stepper.VerificationError;
|
||||
import com.keyman.engine.BaseActivity;
|
||||
import com.keyman.engine.KMManager;
|
||||
import com.keyman.engine.data.Keyboard;
|
||||
import com.keyman.engine.packages.PackageProcessor;
|
||||
|
|
@ -29,7 +29,7 @@ import java.util.List;
|
|||
* Also available during keyboard package installation that involves a language choice.
|
||||
* Displays a list of available language names for the user to add for a given installed packageID/keyboardID.
|
||||
*/
|
||||
public class SelectLanguageActivity extends AppCompatActivity implements
|
||||
public class SelectLanguageActivity extends BaseActivity implements
|
||||
StepperLayout.StepperListener, SelectLanguageFragment.OnLanguagesSelectedListener {
|
||||
private final static String TAG = "SelectLanguageActivity";
|
||||
private File packagePath;
|
||||
|
|
@ -43,6 +43,7 @@ public class SelectLanguageActivity extends AppCompatActivity implements
|
|||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_select_language);
|
||||
setupEdgeToEdge(R.id.select_language_layout);
|
||||
|
||||
Bundle bundle = getIntent().getExtras();
|
||||
keyboard = (Keyboard)bundle.getSerializable("keyboard");
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@ import android.widget.ListView;
|
|||
import android.widget.SimpleAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
import com.keyman.engine.BaseActivity;
|
||||
import com.keyman.engine.KMManager;
|
||||
import com.keyman.engine.data.Keyboard;
|
||||
import com.keyman.engine.data.KeyboardController;
|
||||
|
|
@ -33,7 +33,7 @@ import java.util.List;
|
|||
* Keyman Settings --> KeymanInstallActivity --> SelectPackageActivity
|
||||
* Displays a list of installed package ID / keyboard IDs so the user can select a language from the kmp.json
|
||||
*/
|
||||
public final class SelectPackageActivity extends AppCompatActivity {
|
||||
public final class SelectPackageActivity extends BaseActivity {
|
||||
private static final String TAG = "SelectPackageActivity";
|
||||
private static ArrayList<HashMap<String, String>> list = null;
|
||||
private static Typeface titleFont = null;
|
||||
|
|
@ -49,6 +49,8 @@ public final class SelectPackageActivity extends AppCompatActivity {
|
|||
context = this;
|
||||
|
||||
setContentView(R.layout.activity_list_layout);
|
||||
setupEdgeToEdge(R.id.activity_list_layout);
|
||||
|
||||
final Toolbar toolbar = findViewById(R.id.list_toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/ms_white"
|
||||
android:pathData="M12,20 L4,8 L20,8 Z" />
|
||||
</vector>
|
||||
10
android/KMAPro/kMAPro/src/main/res/drawable/ic_arrow_up.xml
Normal file
10
android/KMAPro/kMAPro/src/main/res/drawable/ic_arrow_up.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/ms_white"
|
||||
android:pathData="M12,4 L4,16 L20,16 Z" />
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Resize handle bar background with grip lines -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Base background with Keyman blue -->
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/keyman_blue" />
|
||||
<corners
|
||||
android:topLeftRadius="8dp"
|
||||
android:topRightRadius="8dp" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<!-- Grip indicator - three horizontal lines -->
|
||||
<item
|
||||
android:gravity="center"
|
||||
android:top="12dp"
|
||||
android:bottom="12dp">
|
||||
<layer-list>
|
||||
<item android:top="0dp" android:bottom="10dp">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/ms_white" />
|
||||
<corners android:radius="1dp" />
|
||||
<size android:width="40dp" android:height="3dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item android:top="6dp" android:bottom="6dp">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/ms_white" />
|
||||
<corners android:radius="1dp" />
|
||||
<size android:width="40dp" android:height="3dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item android:top="10dp" android:bottom="0dp">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/ms_white" />
|
||||
<corners android:radius="1dp" />
|
||||
<size android:width="40dp" android:height="3dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
</item>
|
||||
</layer-list>
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Resize handle bar with grip lines and touch feedback -->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Pressed state - solid darker background -->
|
||||
<item android:state_pressed="true">
|
||||
<layer-list>
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#0E5196" />
|
||||
<corners
|
||||
android:topLeftRadius="8dp"
|
||||
android:topRightRadius="8dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item
|
||||
android:width="40dp"
|
||||
android:height="4dp"
|
||||
android:gravity="center">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/ms_white" />
|
||||
<corners android:radius="2dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
</item>
|
||||
|
||||
<!-- Default state - gradient background with grip lines -->
|
||||
<item android:drawable="@drawable/resize_handle_background" />
|
||||
</selector>
|
||||
|
|
@ -1,47 +1,115 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:id="@+id/adjust_keyboard_layout"
|
||||
android:fitsSystemWindows="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/titlebar" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/drag_the_keyboard"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/titlebar"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||
android:text="@string/drag_the_keyboard"
|
||||
android:textSize="@dimen/adjust_keyboard_height_label_textsize"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rotate_the_device"
|
||||
android:layout_below="@id/drag_the_keyboard"
|
||||
android:id="@+id/adjust_instructions"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||
android:text="@string/rotate_the_device"
|
||||
android:textSize="@dimen/adjust_keyboard_height_label_textsize"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/reset_to_defaults"
|
||||
style="@style/Widget.AppCompat.Button.Colored"
|
||||
android:background="@color/keyman_blue"
|
||||
android:textColor="@color/ms_white"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/rotate_the_device"
|
||||
android:layout_centerInParent="true"
|
||||
android:singleLine="true"
|
||||
android:layout_marginEnd="@dimen/activity_horizontal_margin"
|
||||
android:layout_marginTop="8dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:text="@string/reset_to_defaults"
|
||||
android:textSize="@dimen/adjust_keyboard_height_label_textsize" />
|
||||
android:text="@string/adjust_keyboard_instructions"
|
||||
android:textSize="@dimen/adjust_keyboard_height_label_textsize"
|
||||
app:layout_constraintTop_toBottomOf="@id/titlebar" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/button_layout"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/adjust_instructions">
|
||||
|
||||
<Button
|
||||
android:id="@+id/reset_to_defaults"
|
||||
style="@style/Widget.AppCompat.Button.Colored"
|
||||
android:background="@color/keyman_blue"
|
||||
android:textColor="@color/ms_white"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:singleLine="true"
|
||||
android:layout_marginTop="8dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:text="@string/reset_to_defaults"
|
||||
android:textSize="@dimen/adjust_keyboard_height_label_textsize"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<!-- Percentage Display -->
|
||||
<TextView
|
||||
android:id="@+id/keyboard_height_percentage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:textSize="14sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="100% Portrait | 100% Landscape"
|
||||
app:layout_constraintTop_toBottomOf="@id/button_layout"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<!-- Grabbable Resize Handle Bar -->
|
||||
<LinearLayout
|
||||
android:id="@+id/resize_handle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:background="@drawable/resize_handle_bar"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:contentDescription="@string/adjust_keyboard_instructions"
|
||||
app:layout_constraintBottom_toTopOf="@id/sample_keyboard"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<!-- Up arrow on left -->
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:src="@drawable/ic_arrow_up"
|
||||
android:contentDescription="Resize up" />
|
||||
|
||||
<!-- Spacer to center -->
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<!-- Center grip lines (visual from background drawable) -->
|
||||
<View
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<!-- Spacer to right -->
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<!-- Down arrow on right -->
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:src="@drawable/ic_arrow_down"
|
||||
android:contentDescription="Resize down" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Resizable keyboard "image -->
|
||||
<ImageView
|
||||
|
|
@ -51,11 +119,7 @@
|
|||
android:contentDescription="@string/adjust_keyboard_height"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/blank_osk"
|
||||
android:background="@drawable/dash_border"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:paddingStart="@dimen/blank_osk_padding"
|
||||
android:paddingEnd="@dimen/blank_osk_padding"
|
||||
android:paddingTop="@dimen/blank_osk_padding"
|
||||
android:paddingBottom="@dimen/blank_osk_padding"/>
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
</RelativeLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -1,75 +1,92 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/info_layout"
|
||||
android:fitsSystemWindows="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/ms_white"
|
||||
tools:context=".InfoActivity" >
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/titlebar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:layout_alignParentTop="true"
|
||||
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/titlebar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/close_button" >
|
||||
|
||||
<!-- Navigation buttons in a LinearLayout to mirror on RTL -->
|
||||
<ImageButton
|
||||
android:id="@+id/back_button"
|
||||
style="?android:attr/actionButtonStyle"
|
||||
app:tint="@color/image_button_color"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:contentDescription="@string/label_back"
|
||||
android:src="@drawable/ic_action_back" />
|
||||
<!-- Navigation buttons in a LinearLayout to mirror on RTL -->
|
||||
<ImageButton
|
||||
android:id="@+id/back_button"
|
||||
style="?android:attr/actionButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start"
|
||||
android:contentDescription="@string/label_back"
|
||||
android:src="@drawable/ic_action_back"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:tint="@color/image_button_color" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/forward_button"
|
||||
style="?android:attr/actionButtonStyle"
|
||||
app:tint="@color/image_button_color"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/label_forward"
|
||||
android:src="@drawable/ic_action_forward" />
|
||||
</LinearLayout>
|
||||
<ImageButton
|
||||
android:id="@+id/forward_button"
|
||||
style="?android:attr/actionButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:contentDescription="@string/label_forward"
|
||||
android:src="@drawable/ic_action_forward"
|
||||
app:layout_constraintStart_toEndOf="@id/back_button"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:tint="@color/image_button_color" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/close_button"
|
||||
style="?android:attr/actionButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:contentDescription="@string/label_close"
|
||||
android:src="@drawable/ic_action_close" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
<ImageButton
|
||||
android:id="@+id/close_button"
|
||||
style="?android:attr/actionButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="end"
|
||||
android:contentDescription="@string/label_close"
|
||||
android:src="@drawable/ic_action_close"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<WebView
|
||||
android:id="@+id/infoWebView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/titlebar"
|
||||
android:layout_marginBottom="@dimen/info_label_height" />
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
<!-- layout_height 0dp to fill the remaining area -->
|
||||
<WebView
|
||||
android:id="@+id/infoWebView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_below="@id/titlebar"
|
||||
android:layout_marginStart="4dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/titlebar"
|
||||
app:layout_constraintBottom_toTopOf="@+id/infoVersion"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<!-- Version string on the bottom of the page -->
|
||||
<TextView
|
||||
android:id="@+id/infoVersion"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/info_label_height"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="@string/title_version"
|
||||
android:textSize="@dimen/info_label_textsize"
|
||||
android:layout_alignParentBottom="true" />
|
||||
android:id="@id/infoVersion"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:gravity="center"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="@string/title_version"
|
||||
android:textSize="@dimen/info_label_textsize"
|
||||
android:layout_alignParentBottom="true"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
</RelativeLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/kmp_browser_layout"
|
||||
android:fitsSystemWindows="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".KMPBrowserActivity"
|
||||
|
|
@ -9,6 +12,8 @@
|
|||
<WebView
|
||||
android:id="@+id/kmpBrowserWebView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</RelativeLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
|||
|
|
@ -1,35 +1,42 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/constraintLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
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" />
|
||||
<include layout="@layout/titlebar"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<include layout="@layout/check_chrome_webview_layout"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/titlebar"/>
|
||||
<include
|
||||
layout="@layout/check_chrome_webview_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/titlebar"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toBottomOf="@id/titlebar" />
|
||||
|
||||
<com.keyman.engine.KMTextView
|
||||
android:id="@+id/kmTextView"
|
||||
android:background="@drawable/textview_bg"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/checkWebViewChromeLayout"
|
||||
android:background="@drawable/textview_bg"
|
||||
android:ems="10"
|
||||
android:inputType="textMultiLine|textNoSuggestions"
|
||||
android:hint="@string/textview_hint"
|
||||
android:gravity="top"
|
||||
android:scrollbars="vertical">
|
||||
android:hint="@string/textview_hint"
|
||||
android:inputType="textMultiLine|textNoSuggestions"
|
||||
android:scrollbars="vertical"
|
||||
app:layout_constraintTop_toBottomOf="@id/checkWebViewChromeLayout">
|
||||
|
||||
<requestFocus />
|
||||
</com.keyman.engine.KMTextView>
|
||||
|
||||
</RelativeLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/package_installer_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
|
|
@ -23,6 +24,17 @@
|
|||
app:ms_nextButtonText="@string/label_next"
|
||||
app:ms_completeButtonColor="@color/keyman_blue"
|
||||
app:ms_completeButtonText="@string/label_ok"
|
||||
app:ms_stepperType="dots" />
|
||||
app:ms_stepperType="dots"
|
||||
app:layout_constraintTop_toBottomOf="@+id/titlebar"
|
||||
app:layout_constraintBottom_toTopOf="@+id/bottom_navigation"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</RelativeLayout>
|
||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
android:id="@id/bottom_navigation"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:fitsSystemWindows="true"
|
||||
android:id="@+id/select_language_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
|
|
@ -15,7 +17,7 @@
|
|||
android:id="@+id/stepperLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_below="@+id/titlebar"
|
||||
app:ms_showBackButtonOnFirstStep="true"
|
||||
app:ms_backButtonColor="@color/keyman_blue"
|
||||
app:ms_backButtonText="@string/label_back"
|
||||
|
|
@ -23,6 +25,18 @@
|
|||
app:ms_nextButtonText="@string/label_next"
|
||||
app:ms_completeButtonColor="@color/keyman_blue"
|
||||
app:ms_completeButtonText="@string/label_ok"
|
||||
app:ms_stepperType="dots" />
|
||||
app:ms_stepperType="dots"
|
||||
app:layout_constraintTop_toBottomOf="@id/titlebar"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/bottom_navigation" />
|
||||
|
||||
</RelativeLayout>
|
||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
android:id="@+id/bottom_navigation"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintVertical_bias="1.0"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
android:background="@android:color/white"
|
||||
android:gravity="start"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:text="@string/text_require_chrome_57"
|
||||
android:text="@string/text_require_chrome_version"
|
||||
android:layout_marginStart="@dimen/button_playstore_margin"
|
||||
android:textSize="@dimen/update_chrome_label_textsize" />
|
||||
|
||||
|
|
|
|||
|
|
@ -1,23 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/keyman_settings_layout"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/keyman_settings_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:elevation="0dp">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/keyman_settings_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="@color/keyman_blue">
|
||||
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="@color/keyman_blue"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<fragment
|
||||
android:layout_marginTop="?attr/actionBarSize"
|
||||
|
|
@ -26,4 +21,4 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</RelativeLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
|||
|
|
@ -116,9 +116,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">ሁሉም ቋንቋዎች ቀደም ሲል ተጭኗል</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">የፊደል ገበታ ሰሌዳውን በመጎተት ቁመቱን አስተካክል</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">ዲቫይሱን በማሽከርከር ፖርትሬቱንና ላንድስኬፑን አስተካክል</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">የፊደል ገበታ ሰሌዳውን በመጎተት ቁመቱን አስተካክል ዲቫይሱን በማሽከርከር ፖርትሬቱንና ላንድስኬፑን አስተካክል</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">ወደ ቀደመው ሴቲንግ መልስ</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -122,9 +122,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">كل اللغات مثبتة مسبقا</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">اسحب لوحة المفاتيح لتغيير مدى ارتفاعها</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">دور الجهاز لضبط الوضع العمودي والوضع الأفقي</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">اسحب لوحة المفاتيح لتغيير مدى ارتفاعها. دور الجهاز لضبط الوضع العمودي والوضع الأفقي.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">إعادة ضبط الافتراضيات</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -114,9 +114,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Bütün dillər artıq quraşdırılıb</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Hündürlüyünün ölcüsü tənzimləmək üçün klaviaturanı dartın</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Portret və landşaft tənzimləmək üçün cihazı çevirin</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Hündürlüyünün ölcüsü tənzimləmək üçün klaviaturanı dartın. Portret və landşaft tənzimləmək üçün cihazı çevirin.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">İlkin vəziyyətinə qaytar</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -120,9 +120,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Ὅλες οἱ γλῶσσες ἔχουν ἤδη ἐγκατασταθεῖ</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Σύρετε τὸ πληκτρολόγιο γιὰ νὰ ἀλλάξετε τὸ ὕψος του</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Περιστρέψτε τὴν συσκευὴ γιὰ προσαρμογὴ τοῦ ὕψους σὲ λειτουργία πορτραίτου καὶ τοπίου</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Σύρετε τὸ πληκτρολόγιο γιὰ νὰ ἀλλάξετε τὸ ὕψος του. Περιστρέψτε τὴν συσκευὴ γιὰ προσαρμογὴ τοῦ ὕψους σὲ λειτουργία πορτραίτου καὶ τοπίου.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Ἐπαναφέρετε τὶς προεπιλεγμένες ρυθμίσεις</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
|
|
|
|||
|
|
@ -120,9 +120,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Ya están instalados todos los idiomas</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Arrastra el teclado para ajustar la altura</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Rota el dispositivo para ajustar el modo vertical y horizontal</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Arrastra el teclado para ajustar la altura. Rota el dispositivo para ajustar el modo vertical y horizontal.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Restaurar valores predeterminados</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
|
|
|
|||
|
|
@ -117,9 +117,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Luggat chatta dakhalo</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Ajubdal keyboard ley badalan tula</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Barrumal shu\'ul ley badalan shaba binshaf</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Ajubdal keyboard ley badalan tula. Barrumal shu\'ul ley badalan shaba binshaf.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Eingalab ley shaba gabul</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -114,9 +114,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Hulfur mnyayeri natǝ mdǝku kurtǝ ka hara instal ɗa</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Ndǝkǝr keyboard kamnyar jaktǝ wala kǝmta saɓǝlkur ni</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Shinar waya wala hansetu ni kamnyar katsa nzi thlat-thlata wala peri-periari</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Ndǝkǝr keyboard kamnyar jaktǝ wala kǝmta saɓǝlkur ni. Shinar waya wala hansetu ni kamnyar katsa nzi thlat-thlata wala peri-periari.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Kalaktǝni katsa ni tsu apa tǝ mdǝ nam ni</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -117,9 +117,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Shanga nyayere ndǝ kurtǝ vu tǝdar</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Ndǝrti kibod akuya ka gǝ kǝnta dzugamkurir ko saɓǝlkur su</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Mwari aka sǝr ʻya su akuyaka gǝ dzuguti njaiyir lanskep</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Ndǝrti kibod akuya ka gǝ kǝnta dzugamkurir ko saɓǝlkur su. Mwari aka sǝr ʻya su akuyaka gǝ dzuguti njaiyir lanskep.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Ɓǝla ʻyati tǝni apa Waci</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -122,9 +122,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Všechny jazyky jsou již nainstalovány</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Přetažením klávesnice změníte výšku</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Otočit zařízení pro nastavení na výšku a na šířku</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Přetažením klávesnice změníte výšku. Otočit zařízení pro nastavení na výšku a na šířku.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Obnovit výchozí nastavení</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
|
|
|
|||
|
|
@ -19,8 +19,9 @@
|
|||
<string name="action_install_updates" comment="Menu notification that keyboard or dictionary updates available">Updates installieren</string>
|
||||
<!-- Context: Title -->
|
||||
<string name="title_version" comment="Title of Keyman for Android version">Version: %1$s</string>
|
||||
<!-- Context: Text label when old Chrome version installed -->
|
||||
<!-- Context: Text label when old Chrome version installed. Label replaced in Keyman 19.0 -->
|
||||
<string name="text_require_chrome_57" comment="Banner text when old version of Chrome is installed"> Keyman benötigt Chrome Version 57 oder neuer.</string>
|
||||
<string name="text_require_chrome_version" comment="Banner text when old version of Chrome is installed"> Keyman benötigt Chrome-Version %1$s oder neuer.</string>
|
||||
<!-- Context: Button label -->
|
||||
<string name="button_update_chrome" comment="Prompt to upgrade Chrome in the Play Store">Chrome aktualisieren</string>
|
||||
<!-- Context: Textfield prompt (… is the ellipsis character "...") -->
|
||||
|
|
@ -68,6 +69,8 @@
|
|||
<!-- Context: Keyman Settings menu -->
|
||||
<string name="adjust_keyboard_height" comment="Menu action to adjust keyboard height">Tastaturhöhe anpassen</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<string name="adjust_longpress_delay" comment="Menu action to adjust longpress delay duration">Langes Drücken anpassen</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<string name="spacebar_caption" comment="Menu action to set the spacebar caption">Leertasten-Beschriftung</string>
|
||||
<!-- Context: Keyman Settings menu / Spacebar caption -->
|
||||
<string name="spacebar_caption_keyboard" comment="Spacebar caption option - keyboard">Tastatur</string>
|
||||
|
|
@ -85,13 +88,17 @@
|
|||
<string name="spacebar_caption_hint_language_keyboard" comment="Spacebar caption hint - language + keyboard">Zeige Sprache und Tastaturnamen auf Leertaste</string>
|
||||
<!-- Context: Keyman Settings menu / Spacebar caption hint -->
|
||||
<string name="spacebar_caption_hint_blank" comment="Spacebar caption hint - blank">Beschriftung auf Leertaste nicht anzeigen</string>
|
||||
<!-- Context: Keyman Settings menu / Show OSK with Physical Keyboard -->
|
||||
<string name="show_osk" comment="show OSK when physical keyboard is connected">Bildschirmtastatur anzeigen</string>
|
||||
<!-- Context: Keyman Settings menu / Show OSK with Physical Keyboard hint -->
|
||||
<string name="show_osk_hint" comment="When physical keyboard is connected">Wenn die physische Tastatur verbunden ist</string>
|
||||
<!-- Context: Keyman Settings menu / Haptic feedback -->
|
||||
<string name="haptic_feedback" comment="haptic feedback on key press">Beim Tippen vibrieren</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<!-- Context: Keyman Settings menu. Removed in Keyman 18 -->
|
||||
<string name="show_banner" comment="text suggestions banner">Banner immer anzeigen</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<!-- Context: Keyman Settings menu. Removed in Keyman 18 -->
|
||||
<string name="show_banner_on" comment="Description when toggle is on">Nicht implementiert</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<!-- Context: Keyman Settings menu. Removed in Keyman 18 -->
|
||||
<string name="show_banner_off" comment="Description when toggle is off">Wenn ausgeschaltet, wird nur angezeigt, wenn vorhersagender Text aktiviert ist</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<string name="show_send_crash_report" comment="permission for sending crash reports">Erlaube das Senden von Fehlerberichten</string>
|
||||
|
|
@ -118,11 +125,19 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Alle Sprachen bereits installiert</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Ziehen Sie die Tastatur, um die Höhe zu ändern</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Drehen Sie das Gerät um Hoch- und Querformat anzupassen</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Ziehen Sie die Tastatur, um die Höhe zu verändern. Drehen Sie das Gerät, um Hoch- oder Querformat anzupassen.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Einstellungen auf Standard zurücksetzen</string>
|
||||
<!-- Context: Keyboard height display format. %1$d = portrait percentage, %2$d = landscape percentage -->
|
||||
<string name="keyboard_height_format" comment="Format for displaying keyboard height percentages">%1$d%% Hochformat | %2$d%% Querformat</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
<string name="longpress_delay_time" comment="Current longpress delay time (seconds)">Verzögerung: %1$.1f Sekunden</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
<string name="ic_delay_time_up" comment="Make longpress delay longer">Verzögerung verlängern</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
<string name="ic_delay_time_down" comment="Make longpress delay shorter">Verzögerung verkürzen</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
<string name="ic_longpress_delay_slider" comment="Adjust longpress delay time">Regler für Verzögerung beim langen Drücken</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
<string name="hint_text" comment="Prompt to type in the browser search bar">Suchen oder URL eingeben</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -120,9 +120,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Todos los idiomas ya están instalados</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Mueve el teclado para reajustar la altura</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Rotar el dispositivo para ajustar a modo vertical u horizontal</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Mueve el teclado para reajustar la altura. Rotar el dispositivo para ajustar a modo vertical u horizontal.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Restablecer valores predeterminados</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
|
|
|
|||
|
|
@ -114,9 +114,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Ɗemngal man fuu arti wa`inni</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Dasa kibod man ngam de`itina juutugo</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Waylutu na`ura man ngam a` de`itina dow bee les</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Dasa kibod man ngam de`itina juutugo. Waylutu na`ura man ngam a` de`itina dow bee les.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Lornu ɗum ko naftirta tum</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -115,9 +115,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Ɗemɗe fof aafaama kisa</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Daas tappirde ndee ngam waylude tooweendi</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Yiiltu kaɓirgol ngol ngam fotndude naddaro e leddaro</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Daas tappirde ndee ngam waylude tooweendi. Yiiltu kaɓirgol ngol ngam fotndude naddaro e leddaro.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Artir e Goowaaɗe</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -118,9 +118,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Toutes les langues de cet ensemble sont déjà installées</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Faites glisser le clavier pour redimensionner la hauteur</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Faire pivoter l\'appareil pour ajuster le portrait et le paysage</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Faites glisser le clavier pour redimensionner la hauteur. Faire pivoter l\'appareil pour ajuster le portrait et le paysage.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Réinitialiser aux valeurs par défaut</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -114,9 +114,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">An riga da an shigar da dukkanin yarukan</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Danne madanni domin sake girman</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Juya na\'urar domin daidaita hoto da gyara shi</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Danne madanni domin sake girman. Juya na\'urar domin daidaita hoto da gyara shi.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Gyara saiti zuwa yadda ya dace</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -115,9 +115,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Sma gwaɗaha luwunda falo</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">An tiɗat keyboard ka virata mbakat fiwuku</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Awava tina ngana ka gulat foto nda plavant bugo</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">An tiɗat keyboard ka virata mbakat fiwuku. Awava tina ngana ka gulat foto nda plavant bugo.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Virata magat ina kol ɗughwana</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -113,9 +113,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Semua bahasa telah terpasang</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Drag the keyboard to resize the height</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Rotate the device to adjust portrait and landscape</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Drag the keyboard to resize the height. Rotate the device to adjust portrait and landscape.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Reset to Defaults</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -19,8 +19,9 @@
|
|||
<string name="action_install_updates" comment="Menu notification that keyboard or dictionary updates available">Installa Aggiornamenti</string>
|
||||
<!-- Context: Title -->
|
||||
<string name="title_version" comment="Title of Keyman for Android version">Versione: %1$s</string>
|
||||
<!-- Context: Text label when old Chrome version installed -->
|
||||
<!-- Context: Text label when old Chrome version installed. Label replaced in Keyman 19.0 -->
|
||||
<string name="text_require_chrome_57" comment="Banner text when old version of Chrome is installed"> Keyman richiede la versione 57 di Chrome o superiore.</string>
|
||||
<string name="text_require_chrome_version" comment="Banner text when old version of Chrome is installed"> Keyman richiede Chrome versione %1$s o superiore.</string>
|
||||
<!-- Context: Button label -->
|
||||
<string name="button_update_chrome" comment="Prompt to upgrade Chrome in the Play Store">Aggiorna Chrome</string>
|
||||
<!-- Context: Textfield prompt (… is the ellipsis character "...") -->
|
||||
|
|
@ -87,6 +88,10 @@
|
|||
<string name="spacebar_caption_hint_language_keyboard" comment="Spacebar caption hint - language + keyboard">Lingua e tastiera nella barra spaziatrice</string>
|
||||
<!-- Context: Keyman Settings menu / Spacebar caption hint -->
|
||||
<string name="spacebar_caption_hint_blank" comment="Spacebar caption hint - blank">Non mostrare la didascalia sulla barra spaziatrice</string>
|
||||
<!-- Context: Keyman Settings menu / Show OSK with Physical Keyboard -->
|
||||
<string name="show_osk" comment="show OSK when physical keyboard is connected">Mostra tastiera a schermo</string>
|
||||
<!-- Context: Keyman Settings menu / Show OSK with Physical Keyboard hint -->
|
||||
<string name="show_osk_hint" comment="When physical keyboard is connected">Quando la tastiera fisica è connessa</string>
|
||||
<!-- Context: Keyman Settings menu / Haptic feedback -->
|
||||
<string name="haptic_feedback" comment="haptic feedback on key press">Vibra durante la digitazione</string>
|
||||
<!-- Context: Keyman Settings menu. Removed in Keyman 18 -->
|
||||
|
|
@ -120,11 +125,11 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Tutte le lingue già installate</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Trascina la tastiera per ridimensionare l\'altezza</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Ruota il dispositivo per regolare verticale e orizzontale</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Trascina la tastiera per ridimensionarne l\'altezza. Ruota il dispositivo per regolare verticale e orizzontale.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Ripristina impostazioni predefinite</string>
|
||||
<!-- Context: Keyboard height display format. %1$d = portrait percentage, %2$d = landscape percentage -->
|
||||
<string name="keyboard_height_format" comment="Format for displaying keyboard height percentages">Altezza %1$d%% | Larghezza %2$d%%</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
<string name="longpress_delay_time" comment="Current longpress delay time (seconds)">Tempo ritardo: %1$.1f secondi</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
|
|
|
|||
|
|
@ -119,9 +119,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">ភាសាទាំងអស់ដែលបានដំឡើងហើយ</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">ទាញក្ដារចុចដើម្បីកែកម្ពស់</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">បង្វិលឧបករណ៍ដើម្បីកែបញ្ឈ និងផ្ដេក</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">ទាញក្ដារចុចដើម្បីកែកម្ពស់ បង្វិលឧបករណ៍ដើម្បីកែបញ្ឈ និងផ្ដេក</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">ប្ដូរកំណត់ទៅលំនាំដើម</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
|
|
|
|||
|
|
@ -118,9 +118,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">ಎಲ್ಲಾ ಭಾಷೆಗಳನ್ನು ಈಗಾಗಲೇ ಸ್ಥಾಪಿಸಲಾಗಿದೆ</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">ಎತ್ತರವನ್ನು ಬದಲಾಯಿಸಲು ಕೀಲಿಮಣೆಯನ್ನು ಎಳೆಯಿರಿ</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">ಪೋರ್ಟ್ರೇಟ್ ಮತ್ತು ಲ್ಯಾಂಡ್ಸ್ಕೇಪ್ ಹೊಂದಿಸಲು ಸಾಧನವನ್ನು ತಿರುಗಿಸಿ</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">ಎತ್ತರವನ್ನು ಬದಲಾಯಿಸಲು ಕೀಲಿಮಣೆಯನ್ನು ಎಳೆಯಿರಿ. ಪೋರ್ಟ್ರೇಟ್ ಮತ್ತು ಲ್ಯಾಂಡ್ಸ್ಕೇಪ್ ಹೊಂದಿಸಲು ಸಾಧನವನ್ನು ತಿರುಗಿಸಿ.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">ಡೀಫಾಲ್ಟ್ ಮರುಹೊಂದಿಸಿ</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -114,9 +114,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Tǝlam samma ikke</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Keyboard dǝ girne datǝnzǝ dǝ fulutǝ ro</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Device dǝ suwarne daatanzǝ daata dǝa kuru baatanzǝ dǝa</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Keyboard dǝ girne datǝnzǝ dǝ fulutǝ ro. Device dǝ suwarne daatanzǝ daata dǝa kuru baatanzǝ dǝa.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Bǝlin sarro waltǝne setinne</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -114,9 +114,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Baɗumma naraha na an fa amikwa ŋanna</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Piya keyboard gé fare tsa ne adaliya</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Mbeɗanahe nja nawura ŋanna gé portrait antara landscape</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Piya keyboard gé fare tsa ne adaliya. Mbeɗanahe nja nawura ŋanna gé portrait antara landscape.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Reset to Defaults</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -120,9 +120,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">စုတ်ဘာသာသီုဖအိုတ်တုဲ</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">သ္ဂောံချိၚ်သမၠုၚ် ကဳဗုဒ်ဂှ် ဂြဲညိ</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">ဂွံချိၚ် လံက်သတိက် လံက်ဇမၠိၚ်ဂှ် ဗဂေတ်ကဵုစက်ပိုဲညိ။</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">သ္ဂောံချိၚ်သမၠုၚ် ကဳဗုဒ်ဂှ် ဂြဲညိ ဂွံချိၚ် လံက်သတိက် လံက်ဇမၠိၚ်ဂှ် ဗဂေတ်ကဵုစက်ပိုဲညိ။</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">ကလေၚ်သ္ပနူပကတိ</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
|
|
|
|||
|
|
@ -114,9 +114,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Ca ŋya yer kuku du mji akuya installed</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Cina keyboard gadibar ngilaba dzigamkur ari</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Shina su ari ga ngilaba dzigamkur baɗa ŋga ari</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Cina keyboard gadibar ngilaba dzigamkur ari. Shina su ari ga ngilaba dzigamkur baɗa ŋga ari.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Iri ga munagu alu lagu ginda nur dang</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -114,9 +114,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Alle talen zijn al geïnstalleerd</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Sleep het toetsenbord om de hoogte aan te passen</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Draai het apparaat om portret en liggend aan te passen</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Sleep het toetsenbord om de hoogte aan te passen. Draai het apparaat om portret en liggend aan te passen.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Standaardinstellingen herstellen</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -116,9 +116,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Wszystkie języki są już zainstalowane</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Przeciągnij klawiaturę, aby zmienić wysokość</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Obróć urządzenie, aby dostosować pionowo i poziomo</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Przeciągnij klawiaturę, aby zmienić wysokość. Obróć urządzenie, aby dostosować pionowo i poziomo.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Przywróć domyślne</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -19,8 +19,9 @@
|
|||
<string name="action_install_updates" comment="Menu notification that keyboard or dictionary updates available">Instalar Atualizações</string>
|
||||
<!-- Context: Title -->
|
||||
<string name="title_version" comment="Title of Keyman for Android version">Versão: %1$s</string>
|
||||
<!-- Context: Text label when old Chrome version installed -->
|
||||
<!-- Context: Text label when old Chrome version installed. Label replaced in Keyman 19.0 -->
|
||||
<string name="text_require_chrome_57" comment="Banner text when old version of Chrome is installed"> O Keyman requer a versão 57 ou mais recente.</string>
|
||||
<string name="text_require_chrome_version" comment="Banner text when old version of Chrome is installed"> O Keyman requer a versão Chrome %1$s ou mais recente.</string>
|
||||
<!-- Context: Button label -->
|
||||
<string name="button_update_chrome" comment="Prompt to upgrade Chrome in the Play Store">Atualizar Chrome</string>
|
||||
<!-- Context: Textfield prompt (… is the ellipsis character "...") -->
|
||||
|
|
@ -87,6 +88,10 @@
|
|||
<string name="spacebar_caption_hint_language_keyboard" comment="Spacebar caption hint - language + keyboard">Idioma e teclado na barra de espaço</string>
|
||||
<!-- Context: Keyman Settings menu / Spacebar caption hint -->
|
||||
<string name="spacebar_caption_hint_blank" comment="Spacebar caption hint - blank">Não mostrar legenda na barra de espaço</string>
|
||||
<!-- Context: Keyman Settings menu / Show OSK with Physical Keyboard -->
|
||||
<string name="show_osk" comment="show OSK when physical keyboard is connected">Mostrar Teclado no Ecrã</string>
|
||||
<!-- Context: Keyman Settings menu / Show OSK with Physical Keyboard hint -->
|
||||
<string name="show_osk_hint" comment="When physical keyboard is connected">Quando o teclado físico é conectado</string>
|
||||
<!-- Context: Keyman Settings menu / Haptic feedback -->
|
||||
<string name="haptic_feedback" comment="haptic feedback on key press">Vibrar ao tocar</string>
|
||||
<!-- Context: Keyman Settings menu. Removed in Keyman 18 -->
|
||||
|
|
@ -120,11 +125,11 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Todos os idiomas já instalados</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Arraste o teclado para redimensionar a altura</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Gire o dispositivo para ajustar o retrato e a paisagem</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Arraste o teclado para redimensionar a altura. Gire o dispositivo para ajustar os modos retrato e paisagem.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Restaurar padrões</string>
|
||||
<!-- Context: Keyboard height display format. %1$d = portrait percentage, %2$d = landscape percentage -->
|
||||
<string name="keyboard_height_format" comment="Format for displaying keyboard height percentages">%1$d%% Modo Retrato %2$d%% Modo Paisagem</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
<string name="longpress_delay_time" comment="Current longpress delay time (seconds)">Tempo de atraso: %1$.1f segundos</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
|
|
|
|||
|
|
@ -117,9 +117,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Все языки уже установлены</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Перетащите клавиатуру для изменения высоты</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Повернуть устройство для настройки портрета и ландшафта</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Перетащите клавиатуру для изменения высоты. Повернуть устройство для настройки портрета и ландшафта.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Сброс по умолчанию</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -115,9 +115,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Alla språk är redan installerade</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Dra tangentbordet för att ändra storlek på höjden</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Rotera enheten för att justera stående och liggande</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Dra tangentbordet för att ändra storlek på höjden. Rotera enheten för att justera stående och liggande.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Återställ till standard</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -117,9 +117,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Всі мови вже встановлені</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Перетягніть клавіатуру, щоб змінити розмір висоти</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Поверніть пристрій, щоб змінити портретну та альбомну орієнтацію</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Перетягніть клавіатуру, щоб змінити розмір висоти. Поверніть пристрій, щоб змінити портретну та альбомну орієнтацію.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Відновити параметри за замовчуванням</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -119,9 +119,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Tất cả ngôn ngữ đã được cài đặt rồi</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Kéo bàn phím để điều chỉnh độ cao</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Xoay thiết bị để điều chỉnh độ ngang độ dọc</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Kéo bàn phím để điều chỉnh độ cao. Xoay thiết bị để điều chỉnh độ ngang độ dọc.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Khôi phục mặc định</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
|
|
|
|||
|
|
@ -115,9 +115,7 @@
|
|||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">所有语言已被安装</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">拖动键盘以调整高度</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">旋转设备以调整横竖屏模式</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">拖动键盘以调整高度. 旋转设备以调整横竖屏模式.</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">重置为默认值</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -30,10 +30,13 @@
|
|||
<!-- Context: Title -->
|
||||
<string name="title_version" comment="Title of Keyman for Android version">Version: %1$s</string>
|
||||
|
||||
<!-- Context: Text label when old Chrome version installed -->
|
||||
<!-- Context: Text label when old Chrome version installed. Label replaced in Keyman 19.0 -->
|
||||
<string name="text_require_chrome_57" comment="Banner text when old version of Chrome is installed">
|
||||
Keyman requires Chrome version 57 or newer.</string>
|
||||
|
||||
<string name="text_require_chrome_version" comment="Banner text when old version of Chrome is installed">
|
||||
Keyman requires Chrome version %1$s or newer.</string>
|
||||
|
||||
<!-- Context: Button label -->
|
||||
<string name="button_update_chrome" comment="Prompt to upgrade Chrome in the Play Store">Update Chrome</string>
|
||||
|
||||
|
|
@ -143,6 +146,12 @@
|
|||
<!-- Context: Keyman Settings menu / Spacebar caption hint -->
|
||||
<string name="spacebar_caption_hint_blank" comment="Spacebar caption hint - blank">Do not show caption on spacebar</string>
|
||||
|
||||
<!-- Context: Keyman Settings menu / Show OSK with Physical Keyboard -->
|
||||
<string name="show_osk" comment="show OSK when physical keyboard is connected">Show On-Screen Keyboard</string>
|
||||
|
||||
<!-- Context: Keyman Settings menu / Show OSK with Physical Keyboard hint -->
|
||||
<string name="show_osk_hint" comment="When physical keyboard is connected">When physical keyboard is connected</string>
|
||||
|
||||
<!-- Context: Keyman Settings menu / Haptic feedback -->
|
||||
<string name="haptic_feedback" comment="haptic feedback on key press">Vibrate when typing</string>
|
||||
|
||||
|
|
@ -192,14 +201,14 @@
|
|||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">All languages already installed</string>
|
||||
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Drag the keyboard to resize the height</string>
|
||||
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Rotate the device to adjust portrait and landscape</string>
|
||||
<string name="adjust_keyboard_instructions" comment="Instructions for adjusting keyboard height">Drag the keyboard to resize the height. Rotate the device to adjust portrait and landscape.</string>
|
||||
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Reset to Defaults</string>
|
||||
|
||||
<!-- Context: Keyboard height display format. %1$d = portrait percentage, %2$d = landscape percentage -->
|
||||
<string name="keyboard_height_format" comment="Format for displaying keyboard height percentages">%1$d%% Portrait | %2$d%% Landscape</string>
|
||||
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
<string name="longpress_delay_time" comment="Current longpress delay time (seconds)">Delay Time: %1$.1f seconds</string>
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,14 @@ base {
|
|||
archivesName = "keyman-engine"
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(21)
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk 34
|
||||
compileSdk 35
|
||||
namespace "com.keyman.engine"
|
||||
|
||||
buildFeatures {
|
||||
|
|
@ -21,14 +27,9 @@ android {
|
|||
buildConfig = true
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 34
|
||||
targetSdkVersion 35
|
||||
|
||||
// KEYMAN_VERSION_CODE and KEYMAN_VERSION_NAME from version.gradle but Gradle removes them for libraries
|
||||
buildConfigField "String", "KEYMAN_ENGINE_VERSION_NAME", "\""+KEYMAN_VERSION_NAME+"\""
|
||||
|
|
@ -65,11 +66,13 @@ android {
|
|||
dependencies {
|
||||
// 1.6.0-rc01 needed to resolve https://issuetracker.google.com/issues/238425626
|
||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
|
||||
// material:1.7.0 will need Gradle plugin 7.1.0+
|
||||
implementation 'com.google.android.material:material:1.12.0'
|
||||
implementation 'commons-io:commons-io:2.16.1'
|
||||
implementation 'io.sentry:sentry-android:7.8.0'
|
||||
implementation 'io.sentry:sentry-android:8.19.1'
|
||||
implementation 'androidx.preference:preference:1.2.1'
|
||||
implementation 'androidx.webkit:webkit:1.14.0'
|
||||
|
||||
// Robolectric
|
||||
testImplementation 'androidx.test.ext:junit:1.2.1'
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ function deregisterModel(modelID) {
|
|||
}
|
||||
|
||||
function enableSuggestions(model, suggestionType) {
|
||||
// Set the options first so that KMW's ModelManager can properly handle model enablement states
|
||||
// Set the options first so that KMW's ModelCache can properly handle model enablement states
|
||||
// the moment we actually register the new model.
|
||||
// Use console_debug
|
||||
console_debug('enableSuggestions(model, maySuggest='+suggestionType+')');
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -9,10 +9,19 @@ import android.content.ContextWrapper;
|
|||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.os.LocaleList;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.core.view.WindowCompat;
|
||||
import androidx.core.view.WindowInsetsControllerCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import com.keyman.engine.util.ContextUtils;
|
||||
|
|
@ -22,6 +31,7 @@ import java.util.Locale;
|
|||
|
||||
public class BaseActivity extends AppCompatActivity {
|
||||
private static final String TAG = "BaseActivity";
|
||||
|
||||
static ContextWrapper localeUpdatedContext;
|
||||
|
||||
/**
|
||||
|
|
@ -68,6 +78,58 @@ public class BaseActivity extends AppCompatActivity {
|
|||
return "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Setup the activity for edge-to-edge
|
||||
* https://developer.android.com/develop/ui/views/layout/edge-to-edge-manually
|
||||
* https://stackoverflow.com/questions/57293449/go-edge-to-edge-on-android-correctly-with-windowinsets
|
||||
* @param layoutID - Top level ID of the view
|
||||
*/
|
||||
public void setupEdgeToEdge(int layoutID) {
|
||||
EdgeToEdge.enable(this);
|
||||
View anchor = findViewById(layoutID);
|
||||
ViewCompat.setOnApplyWindowInsetsListener(anchor,
|
||||
(view, windowInsets) -> {
|
||||
// Allocate insets for system bars and display cutout (notch)
|
||||
Insets insets = windowInsets.getInsets(
|
||||
WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
|
||||
|
||||
view.setPadding(
|
||||
insets.left,
|
||||
insets.top,
|
||||
insets.right,
|
||||
insets.bottom);
|
||||
|
||||
KMManager.applyInsetsToKeyboard(
|
||||
KMManager.KeyboardType.KEYBOARD_TYPE_INAPP, insets.left, insets.right, insets.bottom);
|
||||
return windowInsets;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* If Android API < 35, apply colors to the status bar and navigation bar
|
||||
* @param statusBarcolor - int value of the color to use on the status bar.
|
||||
* @param navigationBarColor - int value of the color to use on the navigation bar.
|
||||
*/
|
||||
public void setupStatusBarColors(int statusBarColor, int navigationBarColor) {
|
||||
// Set status bar colors
|
||||
// https://stackoverflow.com/a/79706054
|
||||
Window window = getWindow();
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
WindowInsetsControllerCompat windowInsetsController =
|
||||
WindowCompat.getInsetsController(window, window.getDecorView());
|
||||
windowInsetsController.setAppearanceLightStatusBars(true);
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
||||
window.setStatusBarColor(getColor(statusBarColor));
|
||||
window.setNavigationBarColor(getColor(navigationBarColor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context newBase) {
|
||||
// Override the app locale using the BCP 47 tag from shared preferences
|
||||
|
|
|
|||
|
|
@ -43,19 +43,20 @@ public class DisplayLanguages {
|
|||
new DisplayLanguageType("ar-SA", "(Arabic) العربية"),
|
||||
new DisplayLanguageType("az-AZ", "Azərbaycanca (Azəricə)"),
|
||||
new DisplayLanguageType("bwr-NG", "Bura-Pabir"),
|
||||
new DisplayLanguageType("zh-CN", "中文(简体) (Simplified Chinese)"),
|
||||
new DisplayLanguageType("cs-CZ", "čeština (Czech)"),
|
||||
new DisplayLanguageType("ff-NG", "Ɗemngal Fulfulde"),
|
||||
new DisplayLanguageType("de-DE", "Deutsch (German)"),
|
||||
new DisplayLanguageType("en", "English"),
|
||||
new DisplayLanguageType("es-ES", "Español (Spanish)"),
|
||||
new DisplayLanguageType("es-419", "Español (Spanish - Latin America)"),
|
||||
new DisplayLanguageType("fr-FR", "French"),
|
||||
new DisplayLanguageType("fr-FR", "français (French)"),
|
||||
new DisplayLanguageType("ha-HG", "Hausa"),
|
||||
new DisplayLanguageType("in-ID", "Indonesian"),
|
||||
new DisplayLanguageType("it-IT", "Italiana"),
|
||||
new DisplayLanguageType("de-DE", "German"),
|
||||
new DisplayLanguageType("kn-IN", "ಕನ್ನಡ (Kannada)"),
|
||||
new DisplayLanguageType("kr-NG", "Kanuri"),
|
||||
new DisplayLanguageType("km-KH", "Khmer"),
|
||||
new DisplayLanguageType("km-KH", "ខ្មែរ (Khmer)"),
|
||||
new DisplayLanguageType("ckl-NG", "Kibaku"),
|
||||
new DisplayLanguageType("mfi-NG", "Mandara (Wandala)"),
|
||||
new DisplayLanguageType("mrt-NG", "Marghi"),
|
||||
|
|
@ -67,12 +68,11 @@ public class DisplayLanguages {
|
|||
new DisplayLanguageType("pt-PT", "Português de Portugal"),
|
||||
new DisplayLanguageType("ff-ZA", "Pulaar-Fulfulde"), // or Fulah
|
||||
new DisplayLanguageType("ru-RU", "Русский (Russian)"),
|
||||
new DisplayLanguageType("shu-latn", "Shuwa (Latin)"),
|
||||
new DisplayLanguageType("sv-SE", "svenska (Swedish)"),
|
||||
new DisplayLanguageType("shu-latn", "Shuwa (Latin script)"),
|
||||
new DisplayLanguageType("sv-SE", "Svenska (Swedish)"),
|
||||
new DisplayLanguageType("vi-VN", "Tiếng Việt (Vietnamese)"),
|
||||
new DisplayLanguageType("uk-UA", "Українська (Ukrainian)"),
|
||||
new DisplayLanguageType("hia-NG", "Waha"),
|
||||
new DisplayLanguageType("zh-CN", "中文(简体) (Simplified Chinese)")
|
||||
new DisplayLanguageType("hia-NG", "Waha")
|
||||
};
|
||||
return languages;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,8 @@ public class KMHelpFileActivity extends BaseActivity {
|
|||
}
|
||||
|
||||
setContentView(R.layout.activity_help_file_layout);
|
||||
setupEdgeToEdge(R.id.help_file_layout);
|
||||
setupStatusBarColors(android.R.color.white, android.R.color.white);
|
||||
|
||||
Bundle bundle = getIntent().getExtras();
|
||||
pkgID = "";
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Copyright (C) 2017-2018 SIL International. All rights reserved.
|
||||
/*
|
||||
* Keyman is copyright (C) SIL Global. MIT License.
|
||||
*/
|
||||
|
||||
package com.keyman.engine;
|
||||
|
|
@ -23,6 +23,7 @@ import com.keyman.engine.util.DependencyUtil.LibraryType;
|
|||
import com.keyman.engine.util.FileUtils;
|
||||
import com.keyman.engine.util.KMLog;
|
||||
import com.keyman.engine.util.KMString;
|
||||
import com.keyman.engine.util.WebViewUtils;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
|
|
@ -85,7 +86,8 @@ final class KMKeyboard extends WebView {
|
|||
|
||||
private static String txtFont = "";
|
||||
private static String oskFont = null;
|
||||
private static String keyboardRoot = "";
|
||||
private static String dataRoot = "";
|
||||
private static String packageRoot = "";
|
||||
private final String fontUndefined = "undefined";
|
||||
private GestureDetector gestureDetector;
|
||||
private static ArrayList<OnKeyboardEventListener> kbEventListeners = null;
|
||||
|
|
@ -366,8 +368,7 @@ final class KMKeyboard extends WebView {
|
|||
} else {
|
||||
KMManager.SystemKeyboardWebViewClient.setKeyboardLoaded(false);
|
||||
}
|
||||
|
||||
String htmlPath = "file://" + getContext().getDir("data", Context.MODE_PRIVATE) + "/" + KMManager.KMFilename_KeyboardHtml;
|
||||
String htmlPath = WebViewUtils.buildAssetUrl(KMManager.KMFilename_KeyboardHtml);
|
||||
loadUrl(htmlPath);
|
||||
setBackgroundColor(0);
|
||||
}
|
||||
|
|
@ -453,10 +454,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);
|
||||
}
|
||||
|
|
@ -549,6 +562,7 @@ final class KMKeyboard extends WebView {
|
|||
return oskFont;
|
||||
}
|
||||
|
||||
// REVIEW: this method seems to be unused
|
||||
/**
|
||||
* Return the full path to the special OSK font,
|
||||
* which is with all the keyboard assets at the root app_data folder
|
||||
|
|
@ -634,7 +648,7 @@ final class KMKeyboard extends WebView {
|
|||
}
|
||||
String kbKey = KMString.format("%s_%s", languageID, keyboardID);
|
||||
|
||||
setKeyboardRoot(packageID);
|
||||
setPackageRoot(packageID);
|
||||
|
||||
// Escape single-quoted names for javascript call
|
||||
keyboardName = keyboardName.replaceAll("\'", "\\\\'"); // Double-escaped-backslash b/c regex.
|
||||
|
|
@ -690,20 +704,20 @@ final class KMKeyboard extends WebView {
|
|||
KMManager.getLatestKeyboardFileVersion(getContext(), packageID, keyboardID) : null;
|
||||
}
|
||||
|
||||
setKeyboardRoot(packageID);
|
||||
setPackageRoot(packageID);
|
||||
|
||||
if(kOskFont == null || kOskFont.isEmpty())
|
||||
kOskFont = kFont;
|
||||
|
||||
JSONObject jDisplayFont = makeFontPaths(kFont);
|
||||
JSONObject jOskFont = makeFontPaths(kOskFont);
|
||||
JSONObject jDisplayFont = makeFontObject(kFont);
|
||||
JSONObject jOskFont = makeFontObject(kOskFont);
|
||||
|
||||
txtFont = getFontFilename(jDisplayFont);
|
||||
oskFont = getFontFilename(jOskFont);
|
||||
|
||||
String kbKey = KMString.format("%s_%s", languageID, keyboardID);
|
||||
|
||||
String keyboardPath = makeKeyboardPath(packageID, keyboardID, keyboardVersion);
|
||||
String keyboardUrl = makeKeyboardUrl(packageID, keyboardID, keyboardVersion);
|
||||
|
||||
JSONObject reg = new JSONObject();
|
||||
try {
|
||||
|
|
@ -711,7 +725,7 @@ final class KMKeyboard extends WebView {
|
|||
reg.put("KI", "Keyboard_" + keyboardID);
|
||||
reg.put("KLC", languageID);
|
||||
reg.put("KL", languageName);
|
||||
reg.put("KF", keyboardPath);
|
||||
reg.put("KF", keyboardUrl);
|
||||
reg.put("KP", packageID);
|
||||
|
||||
if (jDisplayFont != null) reg.put("KFont", jDisplayFont);
|
||||
|
|
@ -797,28 +811,31 @@ final class KMKeyboard extends WebView {
|
|||
}
|
||||
|
||||
// Set the base path of the keyboard depending on the package ID
|
||||
private void setKeyboardRoot(String packageID) {
|
||||
private void setPackageRoot(String packageID) {
|
||||
this.dataRoot = WebViewUtils.buildAssetUrl("");
|
||||
if (packageID.equals(KMManager.KMDefault_UndefinedPackageID)) {
|
||||
this.keyboardRoot = (context.getDir("data", Context.MODE_PRIVATE).toString() +
|
||||
File.separator + KMManager.KMDefault_UndefinedPackageID + File.separator);
|
||||
this.packageRoot = this.dataRoot + KMManager.KMDefault_UndefinedPackageID + "/";
|
||||
} else {
|
||||
this.keyboardRoot = (context.getDir("data", Context.MODE_PRIVATE).toString() +
|
||||
File.separator + KMManager.KMDefault_AssetPackages + File.separator + packageID + File.separator);
|
||||
this.packageRoot = this.dataRoot + KMManager.KMDefault_AssetPackages + "/" + packageID + "/";
|
||||
}
|
||||
}
|
||||
|
||||
public String getKeyboardRoot() {
|
||||
return this.keyboardRoot;
|
||||
private String getDataRoot() {
|
||||
return this.dataRoot;
|
||||
}
|
||||
|
||||
private String makeKeyboardPath(String packageID, String keyboardID, String keyboardVersion) {
|
||||
String keyboardPath;
|
||||
private String getPackageRoot() {
|
||||
return this.packageRoot;
|
||||
}
|
||||
|
||||
private String makeKeyboardUrl(String packageID, String keyboardID, String keyboardVersion) {
|
||||
String keyboardUrl = getPackageRoot();
|
||||
if (packageID.equals(KMManager.KMDefault_UndefinedPackageID)) {
|
||||
keyboardPath = getKeyboardRoot() + keyboardID + "-" + keyboardVersion + ".js";
|
||||
keyboardUrl += keyboardID + "-" + keyboardVersion + ".js";
|
||||
} else {
|
||||
keyboardPath = getKeyboardRoot() + keyboardID + ".js";
|
||||
keyboardUrl += keyboardID + ".js";
|
||||
}
|
||||
return keyboardPath;
|
||||
return keyboardUrl;
|
||||
}
|
||||
|
||||
private void sendKMWError(int lineNumber, String sourceId, String message) {
|
||||
|
|
@ -1030,13 +1047,26 @@ final class KMKeyboard extends WebView {
|
|||
}
|
||||
|
||||
/**
|
||||
* Take a font JSON object and adjust to pass to JS
|
||||
* 1. Replace "source" keys for "files" keys
|
||||
* 2. Create full font paths for .ttf or .svg
|
||||
* @param font String font JSON object as a string
|
||||
* @return JSONObject of modified font information with full paths. If font is invalid, return `null`
|
||||
* Create a JSON object consisting of the font family and the URLs of the
|
||||
* font files on the local device.
|
||||
*
|
||||
* The `font` parameter can either be the filename of the font (with an
|
||||
* extension recognized as font), or a Font object or JSON string.
|
||||
* In the former case a new JSON object is created with the font family
|
||||
* derived from the filename, and the font filename prefixed with path
|
||||
* to the fonts.
|
||||
* In the latter case the legacy `sources` key is renamed to `files`.
|
||||
* If `files` is a single string it will be prefixed with the path to the
|
||||
* fonts. If `files` is an array, the array is iterated until finding
|
||||
* the first file with a font extension which is then prefixed with the
|
||||
* path to the fonts.
|
||||
*
|
||||
* @param font A string containing either the font filename or a font JSON
|
||||
* object as a string
|
||||
* @return JSONObject of modified font information with full paths. If font
|
||||
* is invalid, return `null`.
|
||||
*/
|
||||
private JSONObject makeFontPaths(String font) {
|
||||
private JSONObject makeFontObject(String font) {
|
||||
|
||||
if(font == null || font.equals("")) {
|
||||
return null;
|
||||
|
|
@ -1047,14 +1077,13 @@ final class KMKeyboard extends WebView {
|
|||
JSONObject jfont = new JSONObject();
|
||||
jfont.put(KMManager.KMKey_FontFamily, font.substring(0, font.length()-4));
|
||||
JSONArray jfiles = new JSONArray();
|
||||
jfiles.put(keyboardRoot + font);
|
||||
String fontRoot = KMManager.isDefaultFont(font) ? getDataRoot() : getPackageRoot();
|
||||
jfiles.put(fontRoot + font);
|
||||
jfont.put(KMManager.KMKey_FontFiles, jfiles);
|
||||
return jfont;
|
||||
}
|
||||
|
||||
JSONObject fontObj = new JSONObject(font);
|
||||
JSONArray sourceArray;
|
||||
String fontFile;
|
||||
|
||||
// Replace "sources" key with "files"
|
||||
if (fontObj.has(KMManager.KMKey_FontSource)) {
|
||||
|
|
@ -1064,16 +1093,18 @@ final class KMKeyboard extends WebView {
|
|||
|
||||
Object obj = fontObj.get(KMManager.KMKey_FontFiles);
|
||||
if (obj instanceof String) {
|
||||
fontFile = fontObj.getString(KMManager.KMKey_FontFiles);
|
||||
fontObj.put(KMManager.KMKey_FontFiles, keyboardRoot + obj);
|
||||
String fontFile = fontObj.getString(KMManager.KMKey_FontFiles);
|
||||
String fontRoot = KMManager.isDefaultFont(fontFile) ? getDataRoot() : getPackageRoot();
|
||||
fontObj.put(KMManager.KMKey_FontFiles, fontRoot + obj);
|
||||
return fontObj;
|
||||
} else if (obj instanceof JSONArray) {
|
||||
sourceArray = fontObj.optJSONArray(KMManager.KMKey_FontFiles);
|
||||
JSONArray sourceArray = fontObj.optJSONArray(KMManager.KMKey_FontFiles);
|
||||
if (sourceArray != null) {
|
||||
for (int i = 0; i < sourceArray.length(); i++) {
|
||||
fontFile = sourceArray.getString(i);
|
||||
String fontFile = sourceArray.getString(i);
|
||||
if (FileUtils.hasFontExtension(fontFile)) {
|
||||
fontObj.put(KMManager.KMKey_FontFiles, keyboardRoot + fontFile);
|
||||
String fontRoot = KMManager.isDefaultFont(fontFile) ? getDataRoot() : getPackageRoot();
|
||||
fontObj.put(KMManager.KMKey_FontFiles, fontRoot + fontFile);
|
||||
fontObj.remove(KMManager.KMKey_FontSource);
|
||||
return fontObj;
|
||||
}
|
||||
|
|
@ -1082,7 +1113,6 @@ final class KMKeyboard extends WebView {
|
|||
}
|
||||
} catch (JSONException e) {
|
||||
KMLog.LogException(TAG, "Failed to make font for '"+font+"'", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Copyright (C) 2023 SIL International. All rights reserved.
|
||||
/*
|
||||
* Keyman is copyright (C) SIL Global. MIT License.
|
||||
*/
|
||||
package com.keyman.engine;
|
||||
|
||||
|
|
@ -9,9 +9,13 @@ import android.graphics.Bitmap;
|
|||
import android.graphics.RectF;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebResourceRequest;
|
||||
import android.webkit.WebResourceResponse;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.RelativeLayout;
|
||||
import androidx.webkit.WebViewAssetLoader;
|
||||
import androidx.webkit.WebViewAssetLoader.InternalStoragePathHandler;
|
||||
|
||||
import com.keyman.engine.KeyboardEventHandler.EventType;
|
||||
import com.keyman.engine.KMManager;
|
||||
|
|
@ -19,6 +23,7 @@ import com.keyman.engine.KMManager.KeyboardType;
|
|||
import com.keyman.engine.KMManager.SuggestionType;
|
||||
import com.keyman.engine.util.KMLog;
|
||||
import com.keyman.engine.data.Keyboard;
|
||||
import com.keyman.engine.util.WebViewUtils;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
|
@ -31,11 +36,16 @@ public final class KMKeyboardWebViewClient extends WebViewClient {
|
|||
public Context context;
|
||||
private KeyboardType keyboardType;
|
||||
private boolean keyboardLoaded;
|
||||
private WebViewAssetLoader assetLoader;
|
||||
|
||||
KMKeyboardWebViewClient(Context context, KeyboardType keyboardType) {
|
||||
this.context = context;
|
||||
this.keyboardType = keyboardType;
|
||||
this.keyboardLoaded = false;
|
||||
this.assetLoader = new WebViewAssetLoader.Builder()
|
||||
.addPathHandler(WebViewUtils.ASSET_DATA_PATH,
|
||||
new InternalStoragePathHandler(context, context.getDir("data", Context.MODE_PRIVATE)))
|
||||
.build();
|
||||
|
||||
if (keyboardType != KeyboardType.KEYBOARD_TYPE_INAPP && keyboardType != KeyboardType.KEYBOARD_TYPE_SYSTEM) {
|
||||
KMLog.LogError(TAG, String.format("Cannot initialize: Invalid keyboard type: %s", keyboardType.toString()));
|
||||
|
|
@ -58,6 +68,11 @@ public final class KMKeyboardWebViewClient extends WebViewClient {
|
|||
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
|
||||
return this.assetLoader.shouldInterceptRequest(request.getUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url) {
|
||||
Log.d("KMEA", String.format("onPageFinished: [%s] %s", keyboardType.toString(), url));
|
||||
|
|
@ -74,46 +89,44 @@ public final class KMKeyboardWebViewClient extends WebViewClient {
|
|||
kmKeyboard.keyboardSet = false;
|
||||
KMManager.currentLexicalModel = null;
|
||||
|
||||
if (url.startsWith("file")) { // TODO: is this test necessary?
|
||||
this.keyboardLoaded = true;
|
||||
this.keyboardLoaded = true;
|
||||
|
||||
SharedPreferences prefs = context.getSharedPreferences(context.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE);
|
||||
int index = prefs.getInt(KMManager.KMKey_UserKeyboardIndex, 0);
|
||||
if (index < 0) {
|
||||
index = 0;
|
||||
SharedPreferences prefs = context.getSharedPreferences(context.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE);
|
||||
int index = prefs.getInt(KMManager.KMKey_UserKeyboardIndex, 0);
|
||||
Keyboard keyboardInfo = null;
|
||||
if (index >= 0) {
|
||||
keyboardInfo = KMManager.getKeyboardInfo(context, index);
|
||||
}
|
||||
String langId = null;
|
||||
if (keyboardInfo != null) {
|
||||
langId = keyboardInfo.getLanguageID();
|
||||
kmKeyboard.setKeyboard(keyboardInfo);
|
||||
} else {
|
||||
// Revert to default (index 0) or fallback keyboard
|
||||
keyboardInfo = KMManager.getKeyboardInfo(context, 0);
|
||||
if (keyboardInfo == null) {
|
||||
// Don't log to Sentry because some keyboard apps like FV don't install keyboards until the user chooses
|
||||
keyboardInfo = KMManager.getDefaultKeyboard(context);
|
||||
}
|
||||
Keyboard keyboardInfo = KMManager.getKeyboardInfo(context, index);
|
||||
String langId = null;
|
||||
if (keyboardInfo != null) {
|
||||
langId = keyboardInfo.getLanguageID();
|
||||
kmKeyboard.setKeyboard(keyboardInfo);
|
||||
} else {
|
||||
// Revert to default (index 0) or fallback keyboard
|
||||
keyboardInfo = KMManager.getKeyboardInfo(context, 0);
|
||||
if (keyboardInfo == null) {
|
||||
// Don't log to Sentry because some keyboard apps like FV don't install keyboards until the user chooses
|
||||
keyboardInfo = KMManager.getDefaultKeyboard(context);
|
||||
}
|
||||
if (keyboardInfo != null) {
|
||||
langId = keyboardInfo.getLanguageID();
|
||||
kmKeyboard.setKeyboard(keyboardInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KMManager.registerAssociatedLexicalModel(langId);
|
||||
KMManager.registerAssociatedLexicalModel(langId);
|
||||
|
||||
kmKeyboard.showHelpBubbleAfterDelay(2000, true); // check if it should be shown at that time!
|
||||
kmKeyboard.showHelpBubbleAfterDelay(2000, true); // check if it should be shown at that time!
|
||||
|
||||
kmKeyboard.callJavascriptAfterLoad();
|
||||
kmKeyboard.setSpacebarText(KMManager.getSpacebarText());
|
||||
kmKeyboard.callJavascriptAfterLoad();
|
||||
kmKeyboard.setSpacebarText(KMManager.getSpacebarText());
|
||||
|
||||
KeyboardEventHandler.notifyListeners(KMTextView.kbEventListeners, keyboardType, EventType.KEYBOARD_LOADED, null);
|
||||
KeyboardEventHandler.notifyListeners(KMTextView.kbEventListeners, keyboardType, EventType.KEYBOARD_LOADED, null);
|
||||
|
||||
// Special handling for in-app TextView context keymanapp/keyman#3809
|
||||
if (keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP &&
|
||||
KMTextView.activeView != null && KMTextView.activeView.getClass() == KMTextView.class) {
|
||||
KMTextView.updateTextContext();
|
||||
}
|
||||
// Special handling for in-app TextView context keymanapp/keyman#3809
|
||||
if (keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP &&
|
||||
KMTextView.activeView != null && KMTextView.activeView.getClass() == KMTextView.class) {
|
||||
KMTextView.updateTextContext();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Copyright (C) 2017 SIL International. All rights reserved.
|
||||
/*
|
||||
* Keyman is copyright (C) SIL Global. MIT License.
|
||||
*/
|
||||
|
||||
package com.keyman.engine;
|
||||
|
|
@ -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";
|
||||
|
|
@ -393,6 +406,10 @@ public final class KMManager {
|
|||
return getResourceRoot() + KMDefault_LexicalModelPackages + File.separator;
|
||||
}
|
||||
|
||||
public static String getLexicalModelsUrl() {
|
||||
return WebViewUtils.buildAssetUrl(KMDefault_LexicalModelPackages + "/");
|
||||
}
|
||||
|
||||
public static String getCloudDir() {
|
||||
return getResourceRoot() + KMDefault_UndefinedPackageID + File.separator;
|
||||
}
|
||||
|
|
@ -483,6 +500,19 @@ public final class KMManager {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given font is the default font or the default OSK font.
|
||||
* @param fontName String - The font name and extension
|
||||
* @return boolean true if `fontName` is DejaVuSans.ttf or keymanweb-osk.ttf
|
||||
* or null or empty string, false otherwise
|
||||
*/
|
||||
public static boolean isDefaultFont(String fontName) {
|
||||
if (fontName == null || fontName.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
return fontName.equals(KMDefault_KeyboardFont) || fontName.equals(KMFilename_Osk_Ttf_Font);
|
||||
}
|
||||
|
||||
public static void initialize(final Context context, KeyboardType keyboardType) {
|
||||
appContext = context.getApplicationContext();
|
||||
|
||||
|
|
@ -495,6 +525,11 @@ public final class KMManager {
|
|||
didCopyAssets = true;
|
||||
}
|
||||
|
||||
calculateDefaultKeyboardHeights(context);
|
||||
SharedPreferences prefs = context.getSharedPreferences(KMManager.KMEngine_PrefsKey, Context.MODE_PRIVATE);
|
||||
KeyboardHeight_Context_Portrait_Current = prefs.getInt(KMManager.KMKey_KeyboardHeightPortrait, KMManager.KeyboardHeight_Context_Portrait_Default);
|
||||
KeyboardHeight_Context_Landscape_Current = prefs.getInt(KMManager.KMKey_KeyboardHeightLandscape, KMManager.KeyboardHeight_Context_Landscape_Default);
|
||||
|
||||
if (keyboardType == KeyboardType.KEYBOARD_TYPE_UNDEFINED) {
|
||||
String msg = "Cannot initialize: Invalid keyboard type";
|
||||
KMLog.LogError(TAG, msg);
|
||||
|
|
@ -508,12 +543,6 @@ public final class KMManager {
|
|||
migrateCloudKeyboards(appContext);
|
||||
|
||||
CloudDownloadMgr.getInstance().initialize(appContext);
|
||||
|
||||
calculateDefaultKeyboardHeights(context);
|
||||
SharedPreferences prefs = context.getSharedPreferences(KMManager.KMEngine_PrefsKey, Context.MODE_PRIVATE);
|
||||
KeyboardHeight_Context_Portrait_Current = prefs.getInt(KMManager.KMKey_KeyboardHeightPortrait, KMManager.KeyboardHeight_Context_Portrait_Default);
|
||||
KeyboardHeight_Context_Landscape_Current = prefs.getInt(KMManager.KMKey_KeyboardHeightLandscape, KMManager.KeyboardHeight_Context_Landscape_Default);
|
||||
|
||||
}
|
||||
|
||||
public static void executeResourceUpdate(Context aContext)
|
||||
|
|
@ -692,6 +721,11 @@ public final class KMManager {
|
|||
/**
|
||||
* Adjust the keyboard dimensions. If the suggestion banner is active, use the
|
||||
* combined banner height and keyboard height
|
||||
*
|
||||
* Android API 35+ enforces edge-to-edge so we need to determine the navigation bar height (if visible)
|
||||
* so the keyboard isn't covered up by the navigation bar.
|
||||
* In portrait orientation, this affects the keyboard bottom margin.
|
||||
* On phones in landscape orientation, this affects the left/right side of the keyboard
|
||||
* @return RelativeLayout.LayoutParams
|
||||
*/
|
||||
public static RelativeLayout.LayoutParams getKeyboardLayoutParams() {
|
||||
|
|
@ -700,7 +734,54 @@ public final class KMManager {
|
|||
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
|
||||
RelativeLayout.LayoutParams.MATCH_PARENT, bannerHeight + kbHeight);
|
||||
params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
|
||||
return params;
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies padding to the keyboard so it's not covered by system insets
|
||||
* @param {KeyboardType} keyboard
|
||||
* @param left int padding to account for notch/navigation bar on left side of screen
|
||||
* @param right int padding to account for notch/navigation bar on right side of screen
|
||||
* @param bottom int padding to account for navigation bar on bottom of screen
|
||||
*/
|
||||
public static void applyInsetsToKeyboard(KeyboardType keyboardType, int left, int right, int bottom) {
|
||||
if (keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP && InAppKeyboard != null) {
|
||||
applyInsetsPaddingToKeyboardView(InAppKeyboard, left, right, bottom);
|
||||
}
|
||||
|
||||
if (keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM && SystemKeyboard != null) {
|
||||
applyInsetsPaddingToKeyboardView(SystemKeyboard, left, right, bottom);
|
||||
}
|
||||
}
|
||||
|
||||
private static void applyInsetsPaddingToKeyboardView(KMKeyboard keyboard, int left, int right, int bottom) {
|
||||
View parent = (View) keyboard.getParent();
|
||||
if (keyboard.keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP) {
|
||||
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
|
||||
parent.setPadding(
|
||||
left,
|
||||
parent.getPaddingTop(),
|
||||
right,
|
||||
bottom
|
||||
);
|
||||
// No need to change WebView margins; keep height as-is
|
||||
parent.requestLayout();
|
||||
} else {
|
||||
// Fallback: apply on the WebView itself
|
||||
keyboard.setPadding(
|
||||
left,
|
||||
keyboard.getPaddingTop(),
|
||||
right,
|
||||
bottom
|
||||
);
|
||||
keyboard.requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
private static void initKeyboard(Context appContext, KeyboardType keyboardType) {
|
||||
|
|
@ -831,28 +912,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;
|
||||
|
|
@ -1568,8 +1667,9 @@ public final class KMManager {
|
|||
String modelID = lexicalModelInfo.get(KMKey_LexicalModelID);
|
||||
String languageID = lexicalModelInfo.get(KMKey_LanguageID);
|
||||
boolean modelFileExists = true;
|
||||
File modelFile = new File(getLexicalModelsDir(), pkgID + File.separator + modelID + ".model.js");
|
||||
String path = "file://" + modelFile.getAbsolutePath();
|
||||
String modelFilename = pkgID + File.separator + modelID + ".model.js";
|
||||
File modelFile = new File(getLexicalModelsDir(), modelFilename);
|
||||
String url = getLexicalModelsUrl() + modelFilename;
|
||||
|
||||
// Disable sugestions if lexical-model file doesn't exist
|
||||
if (!modelFile.exists()) {
|
||||
|
|
@ -1584,7 +1684,7 @@ public final class KMManager {
|
|||
modelObj.put("id", modelID);
|
||||
languageJSONArray.put(languageID);
|
||||
modelObj.put("languages", languageJSONArray);
|
||||
modelObj.put("path", path);
|
||||
modelObj.put("path", url);
|
||||
modelObj.put("CustomHelpLink", lexicalModelInfo.get(KMKey_CustomHelpLink));
|
||||
} catch (JSONException e) {
|
||||
KMLog.LogException(TAG, "Invalid lexical model to register", e);
|
||||
|
|
@ -2143,7 +2243,12 @@ public final class KMManager {
|
|||
File kmpJSONFile = new File(path);
|
||||
if (!kmpJSONFile.exists()) {
|
||||
if (!KMManager.isTestMode()) {
|
||||
KMLog.LogError(TAG, path + " not found. Returning version 1.0");
|
||||
if (packageID.equals(KMDefault_PackageID)) {
|
||||
// Suppress error log for the default fallback keyboard if it's not installed
|
||||
Log.d(TAG, path + " not found. Returning version 1.0");
|
||||
} else {
|
||||
KMLog.LogError(TAG, path + " not found. Returning version 1.0");
|
||||
}
|
||||
}
|
||||
return "1.0";
|
||||
}
|
||||
|
|
@ -2339,12 +2444,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) {
|
||||
|
|
@ -2361,16 +2465,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2421,6 +2531,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
|
||||
* @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 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+
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Copyright (C) 2020 SIL International. All rights reserved.
|
||||
/*
|
||||
* Keyman is copyright (C) SIL Global. MIT License.
|
||||
*/
|
||||
package com.keyman.engine.data;
|
||||
|
||||
|
|
@ -15,6 +15,7 @@ import com.keyman.engine.util.BCP47;
|
|||
import com.keyman.engine.util.FileUtils;
|
||||
import com.keyman.engine.util.KMLog;
|
||||
import com.keyman.engine.util.KMString;
|
||||
import com.keyman.engine.util.WebViewUtils;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
|
@ -173,24 +174,24 @@ public class Keyboard extends LanguageResource implements Serializable {
|
|||
return o;
|
||||
}
|
||||
|
||||
private String getKeyboardRoot(Context context) {
|
||||
String keyboardRoot = context.getDir("data", Context.MODE_PRIVATE).toString() +
|
||||
File.separator;
|
||||
|
||||
if (packageID.equals(KMManager.KMDefault_UndefinedPackageID)) {
|
||||
return keyboardRoot + KMManager.KMDefault_UndefinedPackageID + File.separator;
|
||||
} else {
|
||||
return keyboardRoot + KMManager.KMDefault_AssetPackages + File.separator + packageID + File.separator;
|
||||
}
|
||||
private String getDataRoot() {
|
||||
return WebViewUtils.buildAssetUrl("");
|
||||
}
|
||||
|
||||
public String getKeyboardPath(Context context) {
|
||||
private String getPackageRoot() {
|
||||
if (packageID.equals(KMManager.KMDefault_UndefinedPackageID)) {
|
||||
return getDataRoot() + KMManager.KMDefault_UndefinedPackageID + "/";
|
||||
}
|
||||
return getDataRoot() + KMManager.KMDefault_AssetPackages + "/" + packageID + "/";
|
||||
}
|
||||
|
||||
private String getKeyboardUrl() {
|
||||
String keyboardID = this.getKeyboardID();
|
||||
String keyboardVersion = this.getVersion();
|
||||
if (packageID.equals(KMManager.KMDefault_UndefinedPackageID)) {
|
||||
return getKeyboardRoot(context) + keyboardID + "-" + keyboardVersion + ".js";
|
||||
return getPackageRoot() + keyboardID + "-" + keyboardVersion + ".js";
|
||||
} else {
|
||||
return getKeyboardRoot(context) + keyboardID + ".js";
|
||||
return getPackageRoot() + keyboardID + ".js";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -202,17 +203,17 @@ public class Keyboard extends LanguageResource implements Serializable {
|
|||
stubObj.put("KI", "Keyboard_" + this.getKeyboardID());
|
||||
stubObj.put("KLC", this.getLanguageID());
|
||||
stubObj.put("KL", this.getLanguageName());
|
||||
stubObj.put("KF", this.getKeyboardPath(context));
|
||||
stubObj.put("KF", this.getKeyboardUrl());
|
||||
stubObj.put("KP", this.getPackageID());
|
||||
|
||||
String displayFont = this.getFont();
|
||||
if(displayFont != null) {
|
||||
stubObj.put("KFont", this.buildDisplayFontObject(displayFont, context));
|
||||
stubObj.put("KFont", this.buildDisplayFontObject(displayFont));
|
||||
}
|
||||
|
||||
String oskFont = this.getOSKFont();
|
||||
if(oskFont != null) {
|
||||
stubObj.put("KOskFont", this.buildDisplayFontObject(oskFont, context));
|
||||
stubObj.put("KOskFont", this.buildDisplayFontObject(oskFont));
|
||||
}
|
||||
|
||||
String displayName = this.getDisplayName();
|
||||
|
|
@ -228,34 +229,33 @@ public class Keyboard extends LanguageResource implements Serializable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Take a font JSON object and adjust to pass to JS
|
||||
* 1. Replace "source" keys for "files" keys
|
||||
* 2. Create full font paths for .ttf or .svg
|
||||
* @param font String font JSON object as a string
|
||||
* @return JSONObject of modified font information with full paths. If font is invalid, return `null`
|
||||
* Create a JSON object consisting of the font family and the font
|
||||
* file URL.
|
||||
*
|
||||
* @param font A string containing the font filename
|
||||
* @return JSONObject of modified font information with full URL. If font
|
||||
* is invalid, return `null`.
|
||||
*/
|
||||
private JSONObject buildDisplayFontObject(String font, Context context) {
|
||||
private JSONObject buildDisplayFontObject(String font) {
|
||||
if(font == null || font.equals("")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String keyboardRoot = this.getKeyboardRoot(context);
|
||||
String fontRoot = KMManager.isDefaultFont(font) ? this.getDataRoot() : this.getPackageRoot();
|
||||
|
||||
try {
|
||||
if (FileUtils.hasFontExtension(font)) {
|
||||
JSONObject jfont = new JSONObject();
|
||||
jfont.put(KMManager.KMKey_FontFamily, font.substring(0, font.length() - 4));
|
||||
JSONArray jfiles = new JSONArray();
|
||||
jfiles.put(keyboardRoot + font);
|
||||
jfiles.put(fontRoot + font);
|
||||
jfont.put(KMManager.KMKey_FontFiles, jfiles);
|
||||
return jfont;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
KMLog.LogException(TAG, "Failed to make font for '"+font+"'", e);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -32,9 +32,38 @@ public final class WebViewUtils {
|
|||
FULL; // WebView installed and enabled
|
||||
}
|
||||
|
||||
// Min version of Chrome for Keyman for Android for EngineWebViewVersionStatus.DEGRADED
|
||||
public static final String KEYMAN_MIN_TARGET_VERSION_DEGRADED_ANDROID_CHROME = "37.0";
|
||||
|
||||
// Min version of Chrome for Keyman for Android (EngineWebViewVersionStatus.FULL)
|
||||
// TODO: Keep this version in sync with resources/build/minimum-versions.inc.sh
|
||||
public static final String KEYMAN_MIN_TARGET_VERSION_ANDROID_CHROME = "95.0";
|
||||
|
||||
private static final String CHROME_INSTALL_PATTERN_FORMATSTR = "^.*Chrome/([\\d.]+).*$";
|
||||
private static final Pattern installPattern = Pattern.compile(CHROME_INSTALL_PATTERN_FORMATSTR);
|
||||
|
||||
/**
|
||||
* Reserved magic domain for loading files from the local device. At runtime
|
||||
* the WebViewAssetLoader will replace the protocol and domain with the
|
||||
* internal storage path.
|
||||
* See https://developer.android.com/reference/androidx/webkit/WebViewAssetLoader
|
||||
*/
|
||||
private static final String MAGIC_DEFAULT_DOMAIN = "https://appassets.androidplatform.net";
|
||||
|
||||
/**
|
||||
* Path under the asset domain where all assets live
|
||||
*/
|
||||
public static final String ASSET_DATA_PATH = "/data/";
|
||||
|
||||
/**
|
||||
* Build a full URL to the provided asset
|
||||
*/
|
||||
public static String buildAssetUrl(String assetPath) {
|
||||
String appendAsset = assetPath == null ? "" :
|
||||
(assetPath.startsWith("/") ? assetPath.substring(1) : assetPath);
|
||||
return WebViewUtils.MAGIC_DEFAULT_DOMAIN + WebViewUtils.ASSET_DATA_PATH + appendAsset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Keyman Engine mode based on the Chrome version.
|
||||
* @param context - The context
|
||||
|
|
@ -52,9 +81,11 @@ public final class WebViewUtils {
|
|||
chromeVersion = getChromeVersion(context, webView);
|
||||
}
|
||||
|
||||
if (FileUtils.compareVersions("37.0", chromeVersion) == FileUtils.VERSION_GREATER) {
|
||||
if (FileUtils.compareVersions(KEYMAN_MIN_TARGET_VERSION_DEGRADED_ANDROID_CHROME, chromeVersion)
|
||||
== FileUtils.VERSION_GREATER) {
|
||||
return EngineWebViewVersionStatus.DISABLED;
|
||||
} else if (FileUtils.compareVersions("57.0", chromeVersion) == FileUtils.VERSION_GREATER) {
|
||||
} else if (FileUtils.compareVersions(KEYMAN_MIN_TARGET_VERSION_ANDROID_CHROME, chromeVersion)
|
||||
== FileUtils.VERSION_GREATER) {
|
||||
return EngineWebViewVersionStatus.DEGRADED;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/help_file_layout"
|
||||
android:fitsSystemWindows="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
|
@ -9,36 +12,28 @@
|
|||
<WebView
|
||||
android:id="@+id/kmHelpFileWebView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_below="@+id/titlebar"
|
||||
android:layout_above="@+id/buttonBar" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/buttonBar"
|
||||
style="?android:attr/buttonBarStyle"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<!-- Spacer to pad buttons to the right -->
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
app:layout_constraintTop_toBottomOf="@id/titlebar"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/finishButton" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/finishButton"
|
||||
android:id="@id/finishButton"
|
||||
style="@style/Widget.AppCompat.Button.Borderless.Colored"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/label_ok"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:layout_gravity="end"
|
||||
android:elevation="1dp"
|
||||
android:singleLine="true"
|
||||
android:layout_marginStart="10dp" android:layout_marginEnd="15dp">
|
||||
<requestFocus/>
|
||||
android:text="@string/label_ok"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="1.0">
|
||||
|
||||
<requestFocus />
|
||||
</Button>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/activity_list_layout"
|
||||
android:fitsSystemWindows="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/list_toolbar" />
|
||||
<include layout="@layout/list_toolbar"
|
||||
android:id="@+id/list_appbar"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<include layout="@layout/list_layout" />
|
||||
<include layout="@layout/list_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@+id/list_appbar"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:fitsSystemWindows="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/list_toolbar" />
|
||||
<include layout="@layout/list_toolbar"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<!-- ListView of installed keyboards. TODO: calculate height -->
|
||||
<include
|
||||
|
|
@ -14,45 +17,35 @@
|
|||
layout="@layout/list_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:layout_above="@+id/keyboard_linear_layout"
|
||||
android:layout_below="@id/list_appbar" />
|
||||
android:layout_above="@+id/modeling_settings_layout"
|
||||
android:layout_below="@id/list_appbar"
|
||||
app:layout_constraintTop_toBottomOf="@id/list_appbar"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/keyboard_linear_layout"
|
||||
android:layout_width="match_parent"
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/add_button"
|
||||
style="?android:attr/actionButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@id/modeling_settings_layout"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Space
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="10" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/add_button"
|
||||
style="?android:attr/actionButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:layout_marginTop="@dimen/fab_margin"
|
||||
android:layout_marginBottom="@dimen/fab_margin"
|
||||
android:layout_weight="1"
|
||||
android:contentDescription="@string/image_button"
|
||||
android:src="@drawable/ic_add_white"
|
||||
app:fabSize="normal" />
|
||||
|
||||
</LinearLayout>
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:contentDescription="@string/image_button"
|
||||
android:src="@drawable/ic_add_white"
|
||||
android:layout_marginEnd="@dimen/fab_margin"
|
||||
android:layout_marginBottom="@dimen/fab_margin"
|
||||
app:fabSize="normal"
|
||||
app:layout_constraintBottom_toTopOf="@+id/modeling_settings_layout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="1.0"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/modeling_settings_layout"
|
||||
android:id="@id/modeling_settings_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginTop="0dp"
|
||||
android:layout_marginBottom="0dp"
|
||||
android:orientation="vertical">
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintVertical_bias="1.0"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<View
|
||||
android:id="@+id/separator"
|
||||
|
|
@ -117,4 +110,4 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/model_picker"/> -->
|
||||
|
||||
</RelativeLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
|||
|
|
@ -1,31 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/languages_list_layout"
|
||||
android:fitsSystemWindows="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/list_toolbar" />
|
||||
<include layout="@layout/list_toolbar"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<include
|
||||
android:id="@+id/keyboard_list_layout"
|
||||
layout="@layout/list_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/list_appbar" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Space
|
||||
android:layout_width="wrap_content"
|
||||
<include
|
||||
android:id="@+id/keyboard_list_layout"
|
||||
layout="@layout/list_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="10" />
|
||||
android:layout_below="@+id/list_appbar"
|
||||
app:layout_constraintTop_toBottomOf="@id/list_appbar"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/add_button"
|
||||
|
|
@ -33,12 +27,13 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:layout_marginTop="@dimen/fab_margin"
|
||||
android:layout_marginBottom="@dimen/fab_margin"
|
||||
android:layout_weight="1"
|
||||
android:contentDescription="@string/image_button"
|
||||
android:src="@drawable/ic_add_white"
|
||||
app:fabSize="normal" />
|
||||
android:layout_marginEnd="@dimen/fab_margin"
|
||||
android:layout_marginBottom="@dimen/fab_margin"
|
||||
app:fabSize="normal"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="1.0"/>
|
||||
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
|||
|
|
@ -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,16 +1,20 @@
|
|||
<com.google.android.material.appbar.AppBarLayout
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/list_appbar"
|
||||
app:theme="@style/AppTheme.ToolbarBackArrow"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:elevation="0dp">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/list_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize">
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bar_title"
|
||||
|
|
@ -32,6 +36,8 @@
|
|||
style="@style/AppTheme.Divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="2dp"
|
||||
android:layout_alignParentBottom="true"/>
|
||||
app:layout_constraintTop_toBottomOf="@id/list_toolbar"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue