Merge branch 'epic/kmc-convert' into feat/developer/kmc-convert

This commit is contained in:
Sabine 2024-10-02 09:08:50 +02:00
commit cc36bfb024
2099 changed files with 108221 additions and 51640 deletions

View file

@ -19,7 +19,7 @@ AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: AllDefinitions
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: MultiLine
BinPackArguments: true
@ -58,7 +58,7 @@ ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true

View file

@ -10,7 +10,7 @@ body:
### Before you start
Before you submit this bug report, have you searched other reports for this bug?
https://github.com/keymanapp/keyman/issues?q=is%3Aopen+is%3Aissue+label%3Abug
https://github.com/keymanapp/keyman/issues?q=is%3Aissue+label%3Abug
* If you find a matching issue, go ahead and add a comment on that issue.
* If you find related issues, make sure you list them below.

View file

@ -8,7 +8,7 @@ body:
attributes:
value: |
Before you submit this feature request, have you searched existing requests?
https://github.com/keymanapp/keyman/issues?q=is%3Aopen+is%3Aissue+label%3Afeat
https://github.com/keymanapp/keyman/issues?q=is%3Aissue+label%3Afeat
* If you find a matching issue, go ahead and add a comment on that issue.
* If you find related issues, make sure you list them below.

View file

@ -0,0 +1,58 @@
name: build-binary-packages
description: |
Build binary packages
inputs:
dist:
description: 'dist to build binary packages for'
required: true
arch:
description: 'the architecture'
required: false
default: 'amd64'
version:
description: 'The Keyman version'
required: true
prerelease_tag:
description: 'The prerelease tag'
required: true
deb_fullname:
description: 'The full name used for the packages'
required: true
deb_email:
description: 'The email address used for the packages'
required: true
runs:
using: 'composite'
steps:
- name: Download Artifacts
uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1
with:
name: keyman-srcpkg
path: artifacts/keyman-srcpkg
- name: Build
uses: sillsdev/gha-ubuntu-packaging@7b56f50d5d5537e9e9cafd3f6139ec6da69cfcda # v1.1
with:
dist: "${{ inputs.dist }}"
platform: "${{ inputs.arch }}"
source_dir: "artifacts/keyman-srcpkg"
sourcepackage: "keyman_${{ inputs.version }}-1.dsc"
deb_fullname: ${{inputs.deb_fullname}}
deb_email: ${{inputs.deb_email}}
prerelease_tag: ${{ inputs.prerelease_tag }}
- name: Output resulting .deb files
shell: bash
run: |
echo '```' >> $GITHUB_STEP_SUMMARY
echo "$(find artifacts/ -name \*.deb)" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
- name: Store binary packages
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
with:
name: keyman-binarypkgs-${{ inputs.dist }}_${{ inputs.arch }}
path: |
artifacts/*
!artifacts/keyman-srcpkg/
if: always()

6
.github/labeler.yml vendored
View file

@ -20,10 +20,8 @@ common/:
- common/**
- resources/**
common/models/: common/models/**
common/models/types/: common/models/types/**
common/models/templates/: common/models/templates/**
common/models/wordbreakers/: common/models/wordbreakers/**
common/models/templates/: web/src/engine/predictive-text/templates/**
common/models/wordbreakers/: web/src/engine/predictive-text/wordbreakers/**
common/resources/: resources/**
common/web/: common/web/**

View file

@ -15,24 +15,33 @@ labels:
# conventional commit / semantic PR styles
#
- label: 'feat'
- label: 'auto'
matcher:
title: '^feat(\(|:)'
- label: 'fix'
title: '^auto(\(|:)'
- label: 'change'
matcher:
title: '^fix(\(|:)'
title: '^change(\(|:)'
- label: 'chore'
matcher:
title: '^chore(\(|:)'
- label: 'docs'
matcher:
title: '^docs(\(|:)'
- label: 'feat'
matcher:
title: '^feat(\(|:)'
- label: 'fix'
matcher:
title: '^fix(\(|:)'
- label: 'refactor'
matcher:
title: '^refactor(\(|:)'
- label: 'auto'
- label: 'style'
matcher:
title: '^auto(\(|:)'
title: '^style(\(|:)'
- label: 'test'
matcher:
title: '^test(\(|:)'
#
# additional meta flags

119
.github/workflows/api-verification.yml vendored Normal file
View file

@ -0,0 +1,119 @@
name: "API Verification"
on:
workflow_run:
workflows: [Ubuntu packaging]
types:
- completed
env:
STATUS_CONTEXT: 'API Verification'
PKG_NAME: 'libkeymancore'
GH_TOKEN: ${{ github.token }}
jobs:
api_verification:
name: Verify API for libkeymancore*.so
runs-on: ubuntu-latest
outputs:
VERSION: ${{ steps.environment_step.outputs.VERSION }}
PRERELEASE_TAG: ${{ steps.environment_step.outputs.PRERELEASE_TAG }}
GIT_SHA: ${{ steps.environment_step.outputs.GIT_SHA }}
GIT_BASE: ${{ steps.environment_step.outputs.GIT_BASE }}
IS_TEST_BUILD: ${{ steps.environment_step.outputs.IS_TEST_BUILD }}
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 }}
steps:
- name: Restore artifacts
uses: actions/cache/restore@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
with:
path: |
artifacts
key: artifacts-key-${{ github.event.workflow_run.id }}
restore-keys: artifacts-key-
- name: Read environment
id: environment_step
run: |
cat artifacts/env >> $GITHUB_OUTPUT
- name: Set pending status on PR builds
id: set_status
if: steps.environment_step.outputs.IS_TEST_BUILD == 'true'
shell: bash
run: |
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
/repos/$GITHUB_REPOSITORY/statuses/${{ steps.environment_step.outputs.GIT_SHA }} \
-f state='pending' \
-f target_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" \
-f description='API verification started' \
-f context="$STATUS_CONTEXT"
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
with:
ref: '${{ steps.environment_step.outputs.GIT_SHA }}'
fetch-depth: 0
clean: false
path: ${{ github.workspace }}/keyman/
- name: Install devscripts
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 }})"
run: |
BIN_PACKAGE=$(ls "${GITHUB_WORKSPACE}/artifacts/" | grep "${PKG_NAME}[0-9]*_${{ steps.environment_step.outputs.VERSION }}-1${{ steps.environment_step.outputs.PRERELEASE_TAG }}+$(lsb_release -c -s)1_amd64.deb")
cd ${{ github.workspace }}/keyman/linux
./scripts/deb-packaging.sh \
--gha \
--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
- name: Archive .symbols file
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.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' }}
steps:
- name: Set success
if: needs.api_verification.result == 'success'
run: |
echo "RESULT=success" >> $GITHUB_ENV
echo "MSG=Package build succeeded" >> $GITHUB_ENV
- name: Set cancelled
if: needs.api_verification.result == 'cancelled'
run: |
echo "RESULT=error" >> $GITHUB_ENV
echo "MSG=Package build cancelled" >> $GITHUB_ENV
- name: Set failure
if: needs.api_verification.result == 'failure'
run: |
echo "RESULT=failure" >> $GITHUB_ENV
echo "MSG=Package build failed" >> $GITHUB_ENV
- name: Set final status
run: |
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
/repos/$GITHUB_REPOSITORY/statuses/${{ needs.api_verification.outputs.GIT_SHA }} \
-f state="$RESULT" \
-f target_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" \
-f description="$MSG" \
-f context="$STATUS_CONTEXT"

View file

@ -24,13 +24,13 @@ jobs:
sourcepackage:
name: Build source package
if: github.repository == 'keymanapp/keyman'
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
outputs:
VERSION: ${{ steps.version_step.outputs.VERSION }}
PRERELEASE_TAG: ${{ steps.prerelease_tag.outputs.PRERELEASE_TAG }}
steps:
- name: Checkout
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c #v3.3.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
with:
ref: '${{ github.event.client_payload.buildSha }}'
@ -109,52 +109,60 @@ jobs:
debian/***/*
if: always()
binary_packages:
name: Build binary packages
binary_packages_released:
name: Build binary packages for released versions
needs: sourcepackage
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
dist: [focal, jammy, mantic, noble]
arch: [amd64]
dist: [focal, jammy, noble]
runs-on: ubuntu-latest
steps:
- name: Download Artifacts
uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
with:
name: keyman-srcpkg
path: artifacts/keyman-srcpkg
ref: '${{ github.event.client_payload.buildSha }}'
sparse-checkout: '.github/actions/'
- name: Build
uses: sillsdev/gha-ubuntu-packaging@1f4b7e7eacb8c82a4d874ee2c371b9bfef7e16ea # v1.0
uses: ./.github/actions/build-binary-packages
with:
dist: "${{ matrix.dist }}"
platform: "${{ matrix.arch }}"
source_dir: "artifacts/keyman-srcpkg"
sourcepackage: "keyman_${{ needs.sourcepackage.outputs.VERSION }}-1.dsc"
dist: ${{ matrix.dist }}
version: ${{ needs.sourcepackage.outputs.VERSION }}
prerelease_tag: ${{ needs.sourcepackage.outputs.PRERELEASE_TAG }}
deb_fullname: ${{env.DEBFULLNAME}}
deb_email: ${{env.DEBEMAIL}}
prerelease_tag: ${{ needs.sourcepackage.outputs.PRERELEASE_TAG }}
- name: Output resulting .deb files
run: |
echo '```' >> $GITHUB_STEP_SUMMARY
echo "$(find artifacts/ -name \*.deb)" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
binary_packages_unreleased:
name: Build binary packages for next Ubuntu version
needs: sourcepackage
continue-on-error: true
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
dist: [oracular]
- name: Store binary packages
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
with:
name: keyman-binarypkgs-${{ matrix.dist }}_${{ matrix.arch }}
path: |
artifacts/*
!artifacts/keyman-srcpkg/
if: always()
ref: '${{ github.event.client_payload.buildSha }}'
sparse-checkout: '.github/actions/'
- name: Build
uses: ./.github/actions/build-binary-packages
with:
dist: ${{ matrix.dist }}
version: ${{ needs.sourcepackage.outputs.VERSION }}
prerelease_tag: ${{ needs.sourcepackage.outputs.PRERELEASE_TAG }}
deb_fullname: ${{env.DEBFULLNAME}}
deb_email: ${{env.DEBEMAIL}}
autopkg_test:
name: Run autopkgtests
needs: [binary_packages]
needs: [binary_packages_released]
runs-on: ubuntu-latest
steps:
@ -183,10 +191,10 @@ jobs:
deb_signing:
name: Sign source and binary packages
needs: [sourcepackage, binary_packages]
needs: [sourcepackage, binary_packages_released, binary_packages_unreleased]
runs-on: ubuntu-latest
environment: "deploy (linux)"
if: github.event.client_payload.isTestBuild == 'false'
if: ${{always() && needs.sourcepackage.result == 'success' && needs.binary_packages_released.result == 'success' && github.event.client_payload.isTestBuild == 'false'}}
steps:
- name: Sign packages
@ -283,69 +291,57 @@ jobs:
done
echo "::endgroup::"
api_verification:
prepare_api_verification:
name: Verify API for libkeymancore.so
needs: [sourcepackage, binary_packages]
needs: [sourcepackage, binary_packages_released]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
with:
ref: '${{ github.event.client_payload.buildSha }}'
fetch-depth: 0
- name: Download Artifacts
uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1
with:
path: artifacts
merge-multiple: true
- name: Install devscripts
uses: ./.github/actions/apt-install
with:
packages: devscripts equivs
- name: Verify API
- name: Save environment
run: |
cd linux
# Extract line containing "Package: libkeymancore1" and then strip "Package: "
PKG_NAME=$(grep -E 'Package: libkeymancore([0-9]+|$)' debian/control)
PKG_NAME="${PKG_NAME#Package: }"
./scripts/deb-packaging.sh \
--gha \
--bin-pkg "${GITHUB_WORKSPACE}/artifacts/${PKG_NAME}_${{ needs.sourcepackage.outputs.VERSION }}-1${{ needs.sourcepackage.outputs.PRERELEASE_TAG }}+jammy1_amd64.deb" \
--git-sha "${{ github.event.client_payload.buildSha }}" \
--git-base "${{ github.event.client_payload.baseRef }}" \
verify 2>> $GITHUB_STEP_SUMMARY
echo "VERSION=${{ needs.sourcepackage.outputs.VERSION }}" > artifacts/env
echo "PRERELEASE_TAG=${{ needs.sourcepackage.outputs.PRERELEASE_TAG }}" >> artifacts/env
echo "GIT_SHA=${{ github.event.client_payload.buildSha }}" >> artifacts/env
echo "GIT_BASE=${{ github.event.client_payload.baseRef }}" >> artifacts/env
echo "IS_TEST_BUILD=${{ github.event.client_payload.isTestBuild }}" >> artifacts/env
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
- name: Archive .symbols file
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
- name: Cache artifacts
uses: actions/cache/save@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
with:
name: libkeymancore.symbols
path: linux/debian/tmp/DEBIAN/symbols
if: always()
path: |
artifacts
key: artifacts-key-${{ github.run_id }}
# We intentionally ignore the results of binary_packages_unreleased
set_status:
name: Set result status on PR builds
needs: [sourcepackage, binary_packages, api_verification, autopkg_test]
needs: [sourcepackage, binary_packages_released, prepare_api_verification, autopkg_test]
runs-on: ubuntu-latest
if: ${{ always() && github.event.client_payload.isTestBuild == 'true' }}
steps:
- name: Set success
if: needs.sourcepackage.result == 'success' && needs.binary_packages.result == 'success' && needs.api_verification.result == 'success' && needs.autopkg_test.result == 'success'
if: needs.sourcepackage.result == 'success' && needs.binary_packages_released.result == 'success' && needs.prepare_api_verification.result == 'success' && needs.autopkg_test.result == 'success'
run: |
echo "RESULT=success" >> $GITHUB_ENV
echo "MSG=Package build succeeded" >> $GITHUB_ENV
- name: Set cancelled
if: needs.sourcepackage.result == 'cancelled' || needs.binary_packages.result == 'cancelled' || needs.api_verification.result == 'cancelled' || needs.autopkg_test.result == 'cancelled'
if: needs.sourcepackage.result == 'cancelled' || needs.binary_packages_released.result == 'cancelled' || needs.prepare_api_verification.result == 'cancelled' || needs.autopkg_test.result == 'cancelled'
run: |
echo "RESULT=error" >> $GITHUB_ENV
echo "MSG=Package build cancelled" >> $GITHUB_ENV
- name: Set failure
if: needs.sourcepackage.result == 'failure' || needs.binary_packages.result == 'failure' || needs.api_verification.result == 'failure' || needs.autopkg_test.result == 'failure'
if: needs.sourcepackage.result == 'failure' || needs.binary_packages_released.result == 'failure' || needs.prepare_api_verification.result == 'failure' || needs.autopkg_test.result == 'failure'
run: |
echo "RESULT=failure" >> $GITHUB_ENV
echo "MSG=Package build failed" >> $GITHUB_ENV

9
.gitignore vendored
View file

@ -3,7 +3,6 @@
/windows/debug/
/windows/lib/
/windows/release/
/windows/src/PathDefines.mak
/windows/src/**/*.err
/windows/src/**/*.Build.CppClean.log
/windows/src/**/*.dproj.local
@ -184,11 +183,3 @@ lcov.info
/keyman*.buildinfo
/keyman*.changes
/keyman*.tar.?z
#Sabine:
# /common/test/keyboards/invalid/source/*.kmx
# /developer/src/test/auto/kmcomp/*.kmn
# /developer/src/test/auto/kmcomp/*.kvk
# /developer/src/test/auto/kmcomp/*.kvk*
# /developer/src/test/auto/kmcomp/*.txt
/linux/mcompile/keymap/X_bak

View file

@ -379,12 +379,18 @@ group.
| Label name | `keyman` :mag_right: | Description |
| --- | --- | --- |
| `auto` | [search][search-auto] | For PRs only: automatically-opened PRs, e.g. opened by CI. |
| `bug` | [search][search-bug] | For issues only: confirmed bugs or reports that are very likely to be bugs. PRs use `fix` to mark a bug fix. |
| `change` | [search][search-change] | Minor change in functionality, but not new. |
| `chore` | [search][search-chore] | Cleanup work, maintenance, without change in functionality. |
| `docs` | [search][search-docs] | Relating to any type of documentation. |
| `feat` | [search][search-feat] | Feature requests. |
| `bug` | [search][search-bug] | Confirmed bugs or reports that are very likely to be bugs. |
| `chore` | [search][search-chore] | Minor updates to Keyman. |
| `fix` | [search][search-fix] | For PRs only: a bug fix, corresponds to issue label `bug`. |
| `question` | [search][search-question] | Questions more than bug reports or feature requests (e.g. how do I do X). [**Usually better on the Keyman Community**](https://community.software.sil.org/c/keyman) |
| `refactor` | [search][search-refactor] | Code reorganization and refactoring, without change in functionality. |
| `spec` | [search][search-spec] | Issues which are specifications for a large scale feature. |
| `docs` | [search][search-docs] | Related to any type of documentation. |
| `style` | [search][search-style] | Code formatting only. |
| `test` | [search][search-test] | Relating to automated tests. |
#### Issue State
@ -397,7 +403,6 @@ group.
| `low-priority` | [search][search-low-priority] | The Keyman core team has decided not to fix these issues for now, but may in the future as time permits. |
| `wontfix` | [search][search-wontfix] | The Keyman core team has decided not to fix these issues for now, either because they're working as intended or for some other reason. |
| `invalid` | [search][search-invalid] | Issues which aren't valid (e.g. user errors). |
| `auto` | [search][search-auto] | Pull requests created automatically, e.g. version increments. |
| `compatibility` | [search][search-compatibility] | Related to interactions with other applications. |
| `dependencies` | [search][search-dependencies] | Related to changes in dependencies, often automatically generated. |
| `external` | [search][search-external] | Related to issues that require changes to third party applications in order to be resolved. |
@ -408,15 +413,16 @@ These labels also include sublabels, such as `windows/config/`.
| Label name | `keyman` :mag_right: | Description |
| --- | --- | --- |
| `windows/` | [search][search-windows] | Related to Keyman running on Windows. |
| `android/` | [search][search-android] | Related to Keyman running on Android. |
| `common/` | [search][search-common] | Related to shared code. |
| `core/` | [search][search-core] | Related to Keyman Core. |
| `developer/` | [search][search-developer] | Related to Keyman Developer. |
| `ios/` | [search][search-ios] | Related to Keyman running on iOS. |
| `linux/` | [search][search-linux] | Related to Keyman running on Linux. |
| `mac/` | [search][search-mac] | Related to Keyman running on macOS. |
| `ios/` | [search][search-ios] | Related to Keyman running on iOS. |
| `android/` | [search][search-android] | Related to Keyman running on Android. |
| `web/` | [search][search-web] | Related to KeymanWeb. |
| `developer/` | [search][search-developer] | Related to Keyman Developer. |
| `common/` | [search][search-common] | Related to Keyman Core and shared code. |
| `oem/` | [search][search-oem] | Related to third party projects that are built together with Keyman. |
| `web/` | [search][search-web] | Related to KeymanWeb. |
| `windows/` | [search][search-windows] | Related to Keyman running on Windows. |
#### Pull Request Labels
@ -428,12 +434,17 @@ These labels also include sublabels, such as `windows/config/`.
| `user-test-required` | [search][search-user-test-required] | Pull requests where user testing is incomplete. |
[search-feat]: https://github.com/keymanapp/keyman/labels/feat
[search-auto]: https://github.com/keymanapp/keyman/labels/auto
[search-bug]: https://github.com/keymanapp/keyman/labels/bug
[search-change]: https://github.com/keymanapp/keyman/labels/change
[search-chore]: https://github.com/keymanapp/keyman/labels/chore
[search-docs]: https://github.com/keymanapp/keyman/labels/docs
[search-feat]: https://github.com/keymanapp/keyman/labels/feat
[search-fix]: https://github.com/keymanapp/keyman/labels/fix
[search-question]: https://github.com/keymanapp/keyman/labels/question
[search-spec]: https://github.com/keymanapp/keyman/labels/spec
[search-docs]: https://github.com/keymanapp/keyman/labels/docs
[search-style]: https://github.com/keymanapp/keyman/labels/style
[search-test]: https://github.com/keymanapp/keyman/labels/test
[search-requires-design-work]: https://github.com/keymanapp/keyman/labels/requires-design-work
[search-help-wanted]: https://github.com/keymanapp/keyman/labels/help%20wanted
@ -443,20 +454,20 @@ These labels also include sublabels, such as `windows/config/`.
[search-wontfix]: https://github.com/keymanapp/keyman/labels/wontfix
[search-invalid]: https://github.com/keymanapp/keyman/labels/invalid
[search-auto]: https://github.com/keymanapp/keyman/labels/auto
[search-compatibility]: https://github.com/keymanapp/keyman/labels/compatibility
[search-dependencies]: https://github.com/keymanapp/keyman/labels/dependencies
[search-external]: https://github.com/keymanapp/keyman/labels/external
[search-windows]: https://github.com/keymanapp/keyman/labels/windows%2F
[search-android]: https://github.com/keymanapp/keyman/labels/android%2F
[search-common]: https://github.com/keymanapp/keyman/labels/common%2F
[search-core]: https://github.com/keymanapp/keyman/labels/core%2F
[search-developer]: https://github.com/keymanapp/keyman/labels/developer%2F
[search-ios]: https://github.com/keymanapp/keyman/labels/ios%2F
[search-linux]: https://github.com/keymanapp/keyman/labels/linux%2F
[search-mac]: https://github.com/keymanapp/keyman/labels/mac%2F
[search-ios]: https://github.com/keymanapp/keyman/labels/ios%2F
[search-android]: https://github.com/keymanapp/keyman/labels/android%2F
[search-web]: https://github.com/keymanapp/keyman/labels/web%2F
[search-developer]: https://github.com/keymanapp/keyman/labels/developer%2F
[search-common]: https://github.com/keymanapp/keyman/labels/common%2F
[search-oem]: https://github.com/keymanapp/keyman/labels/oem%2F
[search-web]: https://github.com/keymanapp/keyman/labels/web%2F
[search-windows]: https://github.com/keymanapp/keyman/labels/windows%2F
[search-cherry-pick]: https://github.com/keymanapp/keyman/labels/cherry-pick
[search-refactor]: https://github.com/keymanapp/keyman/labels/refactor

1073
HISTORY.md

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
# License
Copyright (c) 2017-2022 SIL International. All rights reserved.
Copyright (c) 2017-2024 SIL Global. All rights reserved.
Licensed under the MIT License.
@ -10,7 +10,7 @@ Licensed under the MIT License.
The MIT License
Copyright (c) 2017-2022 SIL International
Copyright (c) 2017-2024 SIL Global
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -4,7 +4,7 @@
# License
Copyright (c) SIL International.
Copyright (c) SIL Global.
Keyman is an open source project distributed under the [MIT license](LICENSE.md).

View file

@ -1 +1 @@
18.0.15
18.0.111

2
android/.gitignore vendored
View file

@ -37,8 +37,6 @@ KMEA/**/assets/keymanandroid.js
KMEA/**/assets/keyman.js.map
KMEA/**/assets/keymanweb-webview.js
KMEA/**/assets/keymanweb-webview.js.map
KMEA/**/assets/keymanweb-webview.es5.js
KMEA/**/assets/keymanweb-webview.es5.js.map
KMEA/**/assets/map-polyfill.js
KMEA/**/assets/sentry.min.js
KMEA/**/assets/keyman-sentry.js

View file

@ -2,25 +2,27 @@
buildscript {
repositories {
google()
jcenter()
mavenCentral()
flatDir {
dirs "kMAPro/libs/com/stepstone/stepper/material-stepper/4.3.1/"
}
}
dependencies {
classpath 'com.android.tools.build:gradle:7.4.0'
classpath 'com.android.tools.build:gradle:7.4.2'
// sentry-android-gradle-plugin 2.1.5+ requires AGP 7.0
classpath 'io.sentry:sentry-android-gradle-plugin:2.1.2'
classpath 'io.sentry:sentry-android-gradle-plugin:4.6.0'
classpath 'name.remal:gradle-plugins:1.5.0'
// From jcenter() which could be sunset in future
// 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'
classpath 'com.stepstone.stepper:material-stepper:4.3.1@aar'
}
}
allprojects {
repositories {
maven { url uri("${projectDir}/libs") }
google()
jcenter()
mavenCentral()
maven { url "https://jitpack.io" }
}

View file

@ -1,21 +1,16 @@
#!/usr/bin/env bash
# Build Keyman for Android app (KMAPro)
# set -x
set -eu
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh"
. "${THIS_SCRIPT%/*}/../../resources/build/builder.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
. "$KEYMAN_ROOT/resources/build/build-help.inc.sh"
. "$KEYMAN_ROOT/resources/build/build-download-resources.sh"
# This script runs from its own folder
cd "$THIS_SCRIPT_PATH"
. "$KEYMAN_ROOT/android/KMAPro/build-play-store-notes.inc.sh"
# ################################ Main script ################################
@ -32,6 +27,7 @@ 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." \
"--ci Don't start the Gradle daemon. For CI" \
"--upload-sentry Upload to sentry"
@ -116,3 +112,13 @@ if builder_start_action test; then
builder_finish_action success test
fi
if builder_start_action publish; then
# Copy Release Notes
generateReleaseNotes
# Publish symbols and Keyman for Android to Play Store
./gradlew $DAEMON_FLAG publishSentry publishReleaseApk
builder_finish_action success publish
fi

View file

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View file

@ -2,7 +2,7 @@ plugins {
id 'com.android.application'
id 'io.sentry.android.gradle'
// https://github.com/Triple-T/gradle-play-publisher/issues/947#issuecomment-843634852
id 'com.github.triplet.play' version '3.8.1' apply false
id 'com.github.triplet.play' version '3.9.1' apply false
id 'name.remal.default-plugins'
}
@ -105,12 +105,17 @@ android {
}
// how to configure the sentry android gradle plugin
sentry {
// Disables or enables the automatic configuration of Native symbols
uploadNativeSymbols = true
task publishSentry {
doLast {
println 'Publishing Keyman symbols to Sentry'
sentry {
// Disables or enables the automatic configuration of Native symbols
uploadNativeSymbols = true
// Does or doesn't include the source code of native code for Sentry
includeNativeSources = true
// Does or doesn't include the source code of native code for Sentry
includeNativeSources = true
}
}
}
String env_keys_json_file = System.getenv("keys_json_file")
@ -145,17 +150,16 @@ repositories {
dirs 'libs'
}
google()
jcenter()
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.6.0-rc01'
implementation 'com.google.android.material:material:1.6.0'
implementation 'androidx.appcompat:appcompat:1.6.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:6.9.2'
implementation 'androidx.preference:preference:1.2.0'
implementation 'io.sentry:sentry-android:7.8.0'
implementation 'androidx.preference:preference:1.2.1'
implementation "com.android.installreferrer:installreferrer:2.2"
// Add dependency for generating QR Codes

View file

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.stepstone.stepper</groupId>
<artifactId>material-stepper</artifactId>
<version>4.3.1</version>
<packaging>aar</packaging>
<name>Android Material Stepper</name>
<description>This library allows to use Material steppers inside Android applications.</description>
<url>https://github.com/stepstone-tech/android-material-stepper</url>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
</license>
</licenses>
<developers>
<developer>
<id>zawadz88</id>
<name>Piotr Zawadzki</name>
<email>piotr.zawadzki@stepstone.com</email>
</developer>
</developers>
<scm>
<connection>https://github.com/stepstone-tech/android-material-stepper.git</connection>
<developerConnection>https://github.com/stepstone-tech/android-material-stepper.git</developerConnection>
<url>https://github.com/stepstone-tech/android-material-stepper</url>
</scm>
<dependencies>
<dependency>
<groupId>com.android.support</groupId>
<artifactId>appcompat-v7</artifactId>
<version>25.4.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.7.21</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.assertj</groupId>
<artifactId>assertj-android</artifactId>
<version>1.1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.robolectric</groupId>
<artifactId>robolectric</artifactId>
<version>3.3.1</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>httpclient</artifactId>
<groupId>org.apache.httpcomponents</groupId>
</exclusion>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jre7</artifactId>
<version>1.1.4-3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<version>1.1.4-3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.nhaarman</groupId>
<artifactId>mockito-kotlin</artifactId>
<version>1.4.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View file

@ -7,6 +7,10 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.VIBRATE" />
<!-- API 33 and above -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<!-- Devices running up to Android 12L
Starting in API level 33, this permission has no effect:
https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE -->
@ -293,6 +297,11 @@
android:configChanges="orientation"
android:label="@string/app_name"
android:theme="@style/AppTheme.Base" />
<activity
android:name=".AdjustLongpressDelayActivity"
android:configChanges="orientation"
android:label="@string/app_name"
android:theme="@style/AppTheme.Base" />
<!-- Put other WebViewActivities in a separate process so the Keyboard WebView doesn't lag.
Ref https://stackoverflow.com/questions/40650643/timed-out-waiting-on-iinputcontextcallback-with-custom-keyboard-on-android -->

View file

@ -4,6 +4,7 @@
package com.keyman.android;
import com.tavultesoft.kmapro.AdjustLongpressDelayActivity;
import com.tavultesoft.kmapro.BuildConfig;
import com.tavultesoft.kmapro.DefaultLanguageResource;
import com.tavultesoft.kmapro.KeymanSettingsActivity;
@ -42,6 +43,7 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
private static View inputView = null;
private static ExtractedText exText = null;
private KMHardwareKeyboardInterpreter interpreter = null;
private int lastOrientation = Configuration.ORIENTATION_UNDEFINED;
private static final String TAG = "SystemKeyboard";
@ -56,6 +58,7 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
if (DependencyUtil.libraryExists(LibraryType.SENTRY) && !Sentry.isEnabled()) {
Log.d(TAG, "Initializing Sentry");
SentryAndroid.init(getApplicationContext(), options -> {
options.setEnableAutoSessionTracking(false);
options.setRelease(com.tavultesoft.kmapro.BuildConfig.VERSION_GIT_TAG);
options.setEnvironment(com.tavultesoft.kmapro.BuildConfig.VERSION_ENVIRONMENT);
});
@ -129,7 +132,7 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
@Override
public void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart, candidatesEnd);
KMManager.updateSelectionRange(KMManager.KeyboardType.KEYBOARD_TYPE_SYSTEM, newSelStart, newSelEnd);
KMManager.updateSelectionRange(KMManager.KeyboardType.KEYBOARD_TYPE_SYSTEM);
}
/**
@ -145,6 +148,16 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
KMManager.onStartInput(attribute, restarting);
KMManager.resetContext(KeyboardType.KEYBOARD_TYPE_SYSTEM);
// This method (likely) includes the IME equivalent to `onResume` for `Activity`-based classes,
// making it an important time to detect orientation changes.
Context appContext = getApplicationContext();
int newOrientation = KMManager.getOrientation(appContext);
if(newOrientation != lastOrientation) {
lastOrientation = newOrientation;
Configuration newConfig = this.getResources().getConfiguration();
KMManager.onConfigurationChanged(newConfig);
}
// Temporarily disable predictions on certain fields (e.g. hidden password field or numeric)
int inputType = attribute.inputType;
KMManager.setMayPredictOverride(inputType);
@ -152,7 +165,6 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
KMManager.setBannerOptions(false);
} else if (KMManager.isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM)){
// Check if predictions needs to be re-enabled per Settings preference
Context appContext = getApplicationContext();
Keyboard kbInfo = KMManager.getCurrentKeyboardInfo(appContext);
if (kbInfo != null) {
String langId = kbInfo.getLanguageID();
@ -164,14 +176,20 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
}
}
// Determine special handling for ENTER key
KMManager.setEnterMode(attribute.imeOptions, inputType);
InputConnection ic = getCurrentInputConnection();
if (ic != null) {
ExtractedText icText = ic.getExtractedText(new ExtractedTextRequest(), 0);
if (icText != null) {
/*
We do sometimes receive null `icText.text`, even though
getExtractedText() docs does not list this as a possible
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());
int selStart = icText.startOffset + icText.selectionStart;
int selEnd = icText.startOffset + icText.selectionEnd;
boolean didUpdateSelection = KMManager.updateSelectionRange(KeyboardType.KEYBOARD_TYPE_SYSTEM, selStart, selEnd);
boolean didUpdateSelection = KMManager.updateSelectionRange(KeyboardType.KEYBOARD_TYPE_SYSTEM);
if (!didUpdateText || !didUpdateSelection)
exText = icText;
}
@ -194,12 +212,6 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
super.onUpdateExtractingVisibility(ei);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
KMManager.onConfigurationChanged(newConfig);
}
@Override
public void onConfigureWindow(Window win, boolean isFullscreen, boolean isCandidatesOnly) {
super.onConfigureWindow(win, isFullscreen, isCandidatesOnly);
@ -215,13 +227,12 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
super.onComputeInsets(outInsets);
// We should extend the touchable region so that Keyman sub keys menu can receive touch events outside the keyboard frame
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Point size = new Point(0, 0);
wm.getDefaultDisplay().getSize(size);
Point size = KMManager.getWindowSize(getApplicationContext());
int inputViewHeight = 0;
if (inputView != null)
if (inputView != null) {
inputViewHeight = inputView.getHeight();
}
int bannerHeight = KMManager.getBannerHeight(this);
int kbHeight = KMManager.getKeyboardHeight(this);
@ -237,6 +248,8 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
if (exText != null)
exText = null;
}
// Initialize keyboard options
KMManager.sendOptionsToKeyboard();
}
@Override

View file

@ -0,0 +1,147 @@
/*
* Keyman is copyright (C) SIL International. MIT License.
*
* (Optional description of this file)
*/
package com.tavultesoft.kmapro;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.view.View;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import com.keyman.engine.BaseActivity;
import com.keyman.engine.KMManager;
/**
* Settings menu for adjusting the longpress delay time. The value for the current longpress delay time
* is saved in shared preferences as an integer (milliseconds).
*/
public class AdjustLongpressDelayActivity extends BaseActivity {
private static final String TAG = "AdjustLongpressDelay";
public static final String adjustLongpressDelayKey = "AdjustLongpressDelay";
// Keeps track of the adjusted longpress delay time for saving.
// Internally use milliseconds, but GUI displays seconds
private static int currentDelayTimeMS = KMManager.KMDefault_LongpressDelay; // ms
private static int delayTimeIncrement = 200; // ms
/**
* Convert currentDelayTimeMS to progress
* @return int
*/
private int delayTimeToProgress() {
return (currentDelayTimeMS / delayTimeIncrement) - 1;
}
/**
* Convert progress to currentDelayTimeMS
* @param progress
* @return int (milliseconds)
*/
private int progressToDelayTime(int progress) {
return (progress + 1) * delayTimeIncrement + 100;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Context context = this;
setContentView(R.layout.activity_adjust_longpress_delay);
Toolbar toolbar = (Toolbar) findViewById(R.id.titlebar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(null);
actionBar.setDisplayUseLogoEnabled(false);
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setDisplayShowHomeEnabled(true);
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.keyman_blue)));
}
TextView adjustLongpressDelayActivityTitle = (TextView) findViewById(R.id.bar_title);
String titleStr = getString(R.string.adjust_longpress_delay);
adjustLongpressDelayActivityTitle.setTextColor(ContextCompat.getColor(this, R.color.ms_white));
adjustLongpressDelayActivityTitle.setText(titleStr);
currentDelayTimeMS = KMManager.getLongpressDelay();
TextView adjustLongpressDelayText = (TextView) findViewById(R.id.delayTimeText);
String longpressDelayTextSeconds = String.format(getString(R.string.longpress_delay_time), (float)(currentDelayTimeMS/1000.0));
adjustLongpressDelayText.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
adjustLongpressDelayText.setText(longpressDelayTextSeconds);
final SeekBar seekBar = (SeekBar) findViewById(R.id.seekBar);
seekBar.setProgress(delayTimeToProgress());
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// Do nothing
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// Do nothing
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
// Update the text field.
// The keyboard options will be saved and sent to KeymanWeb when exiting the menu
currentDelayTimeMS = progressToDelayTime(progress);
String longpressDelayTextSeconds = String.format(getString(R.string.longpress_delay_time), (float)(currentDelayTimeMS/1000.0));
adjustLongpressDelayText.setText(longpressDelayTextSeconds);
}
});
findViewById(R.id.delayTimeDownButton).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (currentDelayTimeMS > KMManager.KMMinimum_LongpressDelay) {
currentDelayTimeMS -= delayTimeIncrement;
seekBar.setProgress(delayTimeToProgress());
}
}
});
findViewById(R.id.delayTimeUpButton).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (currentDelayTimeMS < KMManager.KMMaximum_LongpressDelay) {
currentDelayTimeMS += delayTimeIncrement;
seekBar.setProgress(delayTimeToProgress());
}
}
});
}
@Override
public void onBackPressed() {
// Store the longpress delay as a reference
// and then update KeymanWeb with the longpress delay
KMManager.setLongpressDelay(currentDelayTimeMS);
KMManager.sendOptionsToKeyboard();
super.onBackPressed();
}
@Override
public boolean onSupportNavigateUp() {
onBackPressed();
return true;
}
}

View file

@ -173,6 +173,7 @@ public class BookmarksActivity extends BaseActivity {
@Override
public void onBackPressed() {
super.onBackPressed();
finish();
overridePendingTransition(0, android.R.anim.fade_out);
}

View file

@ -26,13 +26,19 @@ public class CheckPermissions {
return permissionsOK;
}
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
permissionsOK = Environment.isExternalStorageManager();
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.TIRAMISU) {
// TODO: Workout scoped storage permission #10659
}
} else {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
// < API 30
permissionsOK = checkPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
// API 30-32
permissionsOK = Environment.isExternalStorageManager() ||
checkPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE);
} else {
// API 33+
//https://developer.android.com/about/versions/13/behavior-changes-13#granular-media-permissions
// Manifest.permission.READ_MEDIA_AUDIO doesn't seem to be needed
permissionsOK = permissionsOK && checkPermission(activity, Manifest.permission.READ_MEDIA_IMAGES);
permissionsOK = permissionsOK && checkPermission(activity, Manifest.permission.READ_MEDIA_VIDEO);
}
return permissionsOK;

View file

@ -105,7 +105,7 @@ public final class KeyboardSettingsActivity extends AppCompatActivity {
hashMap = new HashMap<>();
final String customHelpLink = kbd.getHelpLink();
// Check if app declared FileProvider
icon = String.valueOf(R.drawable.ic_arrow_forward);
icon = String.valueOf(R.drawable.ic_action_forward);
// Don't show help link arrow if File Provider unavailable, or custom help doesn't exist
if ( (customHelpLink != null && !FileProviderUtils.exists(context)) ||
(customHelpLink == null && !packageID.equals(KMManager.KMDefault_UndefinedPackageID)) ) {

View file

@ -28,7 +28,7 @@ public class KeymanSettingsFragment extends PreferenceFragmentCompat {
private static Context context;
private Preference languagesPreference, installKeyboardOrDictionary, displayLanguagePreference,
adjustKeyboardHeight;
adjustKeyboardHeight, adjustLongpressDelay;
private ListPreference spacebarTextPreference;
private CheckBoxPreference setSystemKeyboardPreference;
private CheckBoxPreference setDefaultKeyboardPreference;
@ -99,6 +99,7 @@ public class KeymanSettingsFragment extends PreferenceFragmentCompat {
});
setDefaultKeyboardPreference.setOnPreferenceChangeListener(checkBlocker);
adjustKeyboardHeight = new Preference(context);
adjustKeyboardHeight.setKey(AdjustKeyboardHeightActivity.adjustKeyboardHeightKey);
adjustKeyboardHeight.setTitle(getString(R.string.adjust_keyboard_height));
@ -106,6 +107,13 @@ public class KeymanSettingsFragment extends PreferenceFragmentCompat {
Intent adjustKeyboardHeightIntent = new Intent(context, AdjustKeyboardHeightActivity.class);
adjustKeyboardHeight.setIntent(adjustKeyboardHeightIntent);
adjustLongpressDelay = new Preference(context);
adjustLongpressDelay.setKey(AdjustLongpressDelayActivity.adjustLongpressDelayKey);
adjustLongpressDelay.setTitle(getString(R.string.adjust_longpress_delay));
adjustLongpressDelay.setWidgetLayoutResource(R.layout.preference_duration_icon_layout);
Intent adjustLongpressDelayIntent = new Intent(context, AdjustLongpressDelayActivity.class);
adjustLongpressDelay.setIntent(adjustLongpressDelayIntent);
/* Spacebar Caption Preference */
spacebarTextPreference = new ListPreference(context);
@ -188,8 +196,6 @@ public class KeymanSettingsFragment extends PreferenceFragmentCompat {
sendCrashReportPreference.setSummaryOff(getString(R.string.show_send_crash_report_off));
sendCrashReportPreference.setDefaultValue(true);
screen.addPreference(languagesPreference);
screen.addPreference(installKeyboardOrDictionary);
screen.addPreference(displayLanguagePreference);
@ -197,6 +203,7 @@ public class KeymanSettingsFragment extends PreferenceFragmentCompat {
screen.addPreference(setDefaultKeyboardPreference);
screen.addPreference(adjustKeyboardHeight);
screen.addPreference(adjustLongpressDelay);
screen.addPreference(spacebarTextPreference);
screen.addPreference(hapticFeedbackPreference);

View file

@ -174,7 +174,7 @@ public final class LanguageSettingsActivity extends AppCompatActivity {
updateActiveLexicalModel();
ImageView imageView = (ImageView) layout.findViewById(R.id.image1);
imageView.setImageResource(R.drawable.ic_arrow_forward);
imageView.setImageResource(R.drawable.ic_action_forward);
layout.setEnabled(true);
layout.setOnClickListener(new View.OnClickListener() {
@Override
@ -198,7 +198,7 @@ public final class LanguageSettingsActivity extends AppCompatActivity {
* textView = (TextView) layout.findViewById(R.id.text1);
* textView.setText(getString(R.string.manage_dictionary));
* imageView = (ImageView) layout.findViewById(R.id.image1);
* imageView.setImageResource(R.drawable.ic_arrow_forward);
* imageView.setImageResource(R.drawable.ic_action_forward);
*/
listView.setAdapter(adapter);
@ -292,6 +292,7 @@ public final class LanguageSettingsActivity extends AppCompatActivity {
@Override
public void onBackPressed() {
super.onBackPressed();
finish();
}
@ -345,7 +346,7 @@ public final class LanguageSettingsActivity extends AppCompatActivity {
}
holder.text.setText(kbd.getResourceName());
holder.img.setImageResource(R.drawable.ic_arrow_forward);
holder.img.setImageResource(R.drawable.ic_action_forward);
return convertView;
}

View file

@ -152,6 +152,7 @@ public final class LanguagesSettingsActivity extends AppCompatActivity {
@Override
public void onBackPressed() {
super.onBackPressed();
finish();
}
@ -202,7 +203,7 @@ public final class LanguagesSettingsActivity extends AppCompatActivity {
}
holder.textLang.setText(data.name);
holder.img.setImageResource(R.drawable.ic_arrow_forward);
holder.img.setImageResource(R.drawable.ic_action_forward);
holder.textCount.setText(getContext().getResources().getQuantityString(R.plurals.keyboard_count, data.keyboards.size(), data.keyboards.size()));

View file

@ -141,6 +141,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
checkSendCrashReport();
if (KMManager.getMaySendCrashReport()) {
SentryAndroid.init(context, options -> {
options.setEnableAutoSessionTracking(false);
options.setRelease(com.tavultesoft.kmapro.BuildConfig.VERSION_GIT_TAG);
options.setEnvironment(com.tavultesoft.kmapro.BuildConfig.VERSION_ENVIRONMENT);
});
@ -471,7 +472,8 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
@Override
public void onKeyboardLoaded(KeyboardType keyboardType) {
// Do nothing
// Initialize keyboard options
KMManager.sendOptionsToKeyboard();
}
@Override
@ -602,8 +604,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
if (resourceId > 0)
statusBarHeight = getResources().getDimensionPixelSize(resourceId);
Point size = new Point(0, 0);
getWindowManager().getDefaultDisplay().getSize(size);
Point size = KMManager.getWindowSize(context);
int screenHeight = size.y;
Log.d(TAG, "Main resizeTextView bannerHeight: " + bannerHeight);
textView.setHeight(screenHeight - statusBarHeight - actionBarHeight - bannerHeight - keyboardHeight);
@ -646,7 +647,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
final View textSizeController = inflater.inflate(R.layout.text_size_controller, null);
final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(MainActivity.this);
dialogBuilder.setIcon(R.drawable.ic_light_action_textsize);
dialogBuilder.setTitle(String.format(getString(R.string.text_size), textSize));
dialogBuilder.setTitle(getTextSizeString());
dialogBuilder.setView(textSizeController);
dialogBuilder.setPositiveButton(getString(R.string.label_ok), new DialogInterface.OnClickListener() {
@Override
@ -677,7 +678,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
textSize = progress + minTextSize;
textView.setTextSize((float) textSize);
dialog.setTitle(String.format(getString(R.string.text_size), textSize));
dialog.setTitle(getTextSizeString());
}
});
@ -702,6 +703,16 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
});
}
/**
* Combine a localized string for "Text Size" plus Arabic numerals
* @return String
*/
private String getTextSizeString() {
// Instead of formatting the number, will truncate formatting and concat the actual textSize
String label = getString(R.string.text_size).replace("%1$d", "");
return label + KMString.format(" %d", textSize);
}
private void showClearTextDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(MainActivity.this);
dialogBuilder.setIcon(R.drawable.ic_light_action_trash);
@ -866,9 +877,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
}
public static Drawable getActionBarDrawable(Context context) {
Point size = new Point();
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getSize(size);
Point size = KMManager.getWindowSize(context);
int width = size.x;
TypedValue outValue = new TypedValue();

View file

@ -112,7 +112,8 @@ public class PackageActivity extends AppCompatActivity implements
// Number of languages associated with the first keyboard in a keyboard package.
// lexical-model packages will be 0
final int languageCount = kmpProcessor.getLanguageCount(pkgInfo, PackageProcessor.PP_KEYBOARDS_KEY, 0);
final int languageCount = (keyboardCount > 0) ?
kmpProcessor.getLanguageCount(pkgInfo, PackageProcessor.PP_KEYBOARDS_KEY, 0) : 0;
// Sanity check for keyboard packages
if (pkgTarget.equals(PackageProcessor.PP_TARGET_KEYBOARDS)) {
@ -180,6 +181,7 @@ public class PackageActivity extends AppCompatActivity implements
@Override
public void onBackPressed() {
super.onBackPressed();
finish();
overridePendingTransition(0, android.R.anim.fade_out);
}

View file

@ -81,7 +81,7 @@ public final class SelectPackageActivity extends AppCompatActivity {
HashMap<String, String> hashMap = new HashMap<>();
hashMap.put(titleKey, keyboardName);
hashMap.put(subtitleKey, pkgID);
String icon = String.valueOf(R.drawable.ic_arrow_forward);
String icon = String.valueOf(R.drawable.ic_action_forward);
hashMap.put(iconKey, icon);
list.add(hashMap);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 B

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View file

@ -15,7 +15,7 @@
<item>
<bitmap
android:src="@drawable/sil_logo_blue_2017_1"
android:src="@drawable/sil_logo_color_tai_heritage_pro"
android:gravity="center"/>
</item>

View file

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/ic_navigation_back"
android:tint="@color/keyman_blue" />

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/ic_light_dialog_textsize_down"
android:tint="@android:color/black"
android:autoMirrored="true" />

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/ic_light_dialog_textsize_up"
android:tint="@android:color/black"
android:autoMirrored="true"/>

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M16.24,7.76C15.07,6.59 13.54,6 12,6v6l-4.24,4.24c2.34,2.34 6.14,2.34 8.49,0 2.34,-2.34 2.34,-6.14 -0.01,-8.48zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M0.99,19h2.42l1.27,-3.58h5.65L11.59,19h2.42L8.75,5h-2.5L0.99,19zM5.41,13.39L7.44,7.6h0.12l2.03,5.79H5.41zM23,11v2h-8v-2H23z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="48dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="48dp">
<path android:fillColor="@android:color/white" android:pathData="M0.99,19h2.42l1.27,-3.58h5.65L11.59,19h2.42L8.75,5h-2.5L0.99,19zM5.41,13.39L7.44,7.6h0.12l2.03,5.79H5.41zM20,11h3v2h-3v3h-2v-3h-3v-2h3V8h2V11z"/>
</vector>

View file

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/titlebar" />
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:paddingBottom="25dp"
android:paddingTop="60dp" >
<ImageButton
android:id="@+id/delayTimeDownButton"
style="?android:attr/actionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:contentDescription="@string/ic_delay_time_down"
android:src="@drawable/ic_action_decrement" />
<!-- Seekbar range 0 to 6 corresponds to longpress duration 300 ms to 1500 ms -->
<SeekBar
android:id="@+id/seekBar"
android:layout_width="180dp"
android:layout_height="wrap_content"
android:contentDescription="@string/ic_longpress_delay_slider"
android:progress="1"
android:max="6"
android:layout_gravity="center" />
<ImageButton
android:id="@+id/delayTimeUpButton"
style="?android:attr/actionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:contentDescription="@string/ic_delay_time_up"
android:src="@drawable/ic_action_increment" />
</LinearLayout>
<TextView
android:id="@+id/delayTimeText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/linearLayout"
android:gravity="center_horizontal"
android:layout_marginStart="@dimen/activity_horizontal_margin"
android:text="@string/longpress_delay_time"
android:textSize="@dimen/longpress_delay_label_textsize"
/>
</RelativeLayout>

View file

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/ms_white"
tools:context=".InfoActivity" >
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
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"
@ -15,24 +15,31 @@
android:layout_alignParentTop="true"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
<!-- Navigation buttons -->
<ImageButton
android:id="@+id/back_button"
style="?android:attr/actionButtonStyle"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:contentDescription="@string/label_back"
android:src="@drawable/ic_navigation_back" />
android:orientation="horizontal">
<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_navigation_forward" />
<!-- 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" />
<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/close_button"

View file

@ -34,7 +34,7 @@
android:layout_marginEnd="5dp"
android:layout_weight="1"
android:contentDescription="@string/image_button"
android:src="@drawable/ic_navigation_back" />
android:src="@drawable/ic_action_back" />
<ImageButton
android:id="@+id/forwardButton"
@ -45,7 +45,7 @@
android:layout_marginEnd="5dp"
android:layout_weight="1"
android:contentDescription="@string/image_button"
android:src="@drawable/ic_navigation_forward" />
android:src="@drawable/ic_action_forward" />
<ImageButton
android:id="@+id/bookmarksButton"

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/preference_icon"
android:layout_height="@dimen/preference_icon"
android:src="@drawable/ic_action_timelapse" />

View file

@ -2,4 +2,4 @@
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/preference_icon"
android:layout_height="@dimen/preference_icon"
android:src="@drawable/ic_arrow_forward" />
android:src="@drawable/ic_action_forward" />

View file

@ -15,7 +15,7 @@
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:contentDescription="@string/ic_text_size_down"
android:src="@drawable/ic_light_dialog_textsize_down" />
android:src="@drawable/textsize_decrease" />
<SeekBar
android:id="@+id/seekBar"
@ -32,6 +32,6 @@
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:contentDescription="@string/ic_text_size_up"
android:src="@drawable/ic_light_dialog_textsize_up" />
android:src="@drawable/textsize_increase" />
</LinearLayout>

View file

@ -15,7 +15,6 @@
android:layout_height="match_parent"
android:layout_margin="3dp"
android:padding="4dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_above="@+id/progressBar"
android:weightSum="2"
@ -76,7 +75,6 @@
style="@style/Base.Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="@dimen/keyman_bar_height"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentBottom="true"
android:visibility="visible"

View file

@ -0,0 +1,168 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- app_name removed in 16.0 beta. Set in build.gradle -->
<!-- Context: Menu Action -->
<string name="action_share" comment="Menu action to send text content to another app">مشاركة</string>
<!-- Context: Menu Action -->
<string name="action_web" comment="Menu action to open Keyman browser">متصفح ويب</string>
<!-- Context: Menu Action -->
<string name="action_text_size" comment="Menu action to adjust text size">حجم النص</string>
<!-- Context: Menu Action -->
<string name="action_overflow" comment="Show additional menu items">المزيد</string>
<!-- Context: Menu Action -->
<string name="action_clear_text" comment="Menu action to erase text">محو النص</string>
<!-- Context: Menu Action -->
<string name="action_info" comment="Menu action to display Keyman help page">معلومات</string>
<!-- Context: Menu Action -->
<string name="action_settings" comment="Menu action to open Settings menu">الإعدادات</string>
<!-- Context: Menu Action -->
<string name="action_install_updates" comment="Menu notification that keyboard or dictionary updates available">تنزيل التحديثات</string>
<!-- Context: Title -->
<string name="title_version" comment="Title of Keyman for Android version">إصدار: %1$s</string>
<!-- Context: Text label when old Chrome version installed -->
<string name="text_require_chrome_57" comment="Banner text when old version of Chrome is installed">يتطلب Keyman إصدار 57 من Chrome أو أعلى.</string>
<!-- Context: Button label -->
<string name="button_update_chrome" comment="Prompt to upgrade Chrome in the Play Store">تحديث Chrome</string>
<!-- Context: Textfield prompt (&#8230; is the ellipsis character "...") -->
<string name="textview_hint" comment="Prompt to start typing">ابدأ الكتابة هنا&#8230;</string>
<!-- Context: Splash screen (version/copyright info currently disabled -->
<!-- Context: Splash screen (version/copyright info currently disabled -->
<!-- Context: Text Size dialog -->
<string name="text_size" comment="Current text size (number)">حجم النص: %1$d</string>
<!-- Context: Text Size dialog -->
<string name="ic_text_size_up" comment="Make text bigger">تكبير حجم النص</string>
<!-- Context: Text Size dialog -->
<string name="ic_text_size_down" comment="Make text smaller">تصغير حجم النص</string>
<!-- Context: Text Size dialog -->
<string name="ic_text_size_slider" comment="Adjust text size">Text size slider</string>
<!-- Context: Clear Text dialog -->
<string name="all_text_will_be_cleared" comment="Erase all the text">\nسيتم محو كل النص\n</string>
<!-- Context: Get Started menu -->
<string name="get_started" comment="Menu for getting started">إبدأ الآن</string>
<!-- Context: Get Started menu -->
<string name="add_a_keyboard" comment="Menu item to add a keyboard">اضف لوحة مفاتيح بلغتك</string>
<!-- Context: Get Started menu -->
<string name="enable_system_keyboard" comment="Menu item to enable Keyman as system keyboard">تمكين Keyman لوحة المفاتيح الأساسية للنظام</string>
<!-- Context: Get Started menu -->
<string name="set_keyman_as_default" comment="Menu item to set Keyman as default system keyboard">إعداد Keyman لوحة المفاتيح الإفتراضية</string>
<!-- Context: Get Started menu -->
<string name="more_info" comment="Open the Keyman for Android help page">المزيد من المعلومات</string>
<!-- Context: Get Started menu -->
<string name="show_get_started" comment="Show the &quot;Get Started&quot; menu on startup">أظهر \"%1$s\" عند البدء</string>
<!-- Context: Android Storage Permission -->
<string name="request_storage_permission" comment="Request storage permission to access keyboard packages">لتثبيت حزم لوحات المفاتيح، امنح Keyman الإذن لقراءة التخزين الخارجي.</string>
<!-- Context: Android Storage Permission -->
<string name="storage_permission_denied" comment="Keyboard package installation may fail since Android storage permission not granted">تم رفض أذونات التخزين. قد يفشل تثبيت حزمة لوحة المفاتيح</string>
<string name="storage_permission_denied2" comment="Keyboard package installation failed. Recommend install from local file"> Storage permission request failed. Try Keyman Settings - Install from local file</string>
<!-- Context: Keyman Settings menu -->
<string name="keyman_settings" comment="Settings menu title">الإعدادات</string>
<!-- Context: Keyman Settings menu -->
<plurals name="installed_languages">
<item quantity="zero">اللغات المثبتة (%1$d)</item>
<item quantity="one">اللغات المثبتة (%1$d)</item>
<item quantity="two">اللغات المثبتة (%1$d)</item>
<item quantity="few">اللغات المثبتة (%1$d)</item>
<item quantity="many">اللغات المثبتة (%1$d)</item>
<item quantity="other">اللغات المثبتة (%1$d)</item>
</plurals>
<!-- Context: Keyman Settings menu -->
<string name="install_keyboard_or_dictionary" comment="Menu item to install keyboard or dictionary">تثبيت لوحة المفاتيح او القاموس</string>
<!-- Context: Keyman Settings menu -->
<string name="change_display_language" comment="Menu action to change interface language">لغة العرض</string>
<!-- Context: Keyman Settings menu -->
<string name="adjust_keyboard_height" comment="Menu action to adjust keyboard height">تعديل ارتفاع لوحة المفاتيح</string>
<!-- Context: Keyman Settings menu -->
<string name="spacebar_caption" comment="Menu action to set the spacebar caption">نص مفتاح المسافة</string>
<!-- Context: Keyman Settings menu / Spacebar caption -->
<string name="spacebar_caption_keyboard" comment="Spacebar caption option - keyboard">لوحة المفاتيح</string>
<!-- Context: Keyman Settings menu / Spacebar caption -->
<string name="spacebar_caption_language" comment="Spacebar caption option - language">اللغة</string>
<!-- Context: Keyman Settings menu / Spacebar caption -->
<string name="spacebar_caption_language_keyboard" comment="Spacebar caption option - language + keyboard">اللغة + لوحة المفاتيح</string>
<!-- Context: Keyman Settings menu / Spacebar caption -->
<string name="spacebar_caption_blank" comment="Spacebar caption option - blank">فارغ</string>
<!-- Context: Keyman Settings menu / Spacebar caption hint -->
<string name="spacebar_caption_hint_keyboard" comment="Spacebar caption hint - keyboard">أظهر اسم لوحة المفاتيح على مفتاح المسافة</string>
<!-- Context: Keyman Settings menu / Spacebar caption hint -->
<string name="spacebar_caption_hint_language" comment="Spacebar caption hint - language">أظهر اسم اللغة على المسافة</string>
<!-- Context: Keyman Settings menu / Spacebar caption hint -->
<string name="spacebar_caption_hint_language_keyboard" comment="Spacebar caption hint - language + keyboard">اللغة ولوحة المفاتيح على المسافة</string>
<!-- Context: Keyman Settings menu / Spacebar caption hint -->
<string name="spacebar_caption_hint_blank" comment="Spacebar caption hint - blank">لا تظهر نص على المسافة</string>
<!-- Context: Keyman Settings menu / Haptic feedback -->
<string name="haptic_feedback" comment="haptic feedback on key press">الاهتزاز عند الكتابة</string>
<!-- Context: Keyman Settings menu -->
<string name="show_banner" comment="text suggestions banner">إظهار اللافتة دائماً</string>
<!-- Context: Keyman Settings menu -->
<string name="show_banner_on" comment="Description when toggle is on">سيتم تنفيذها</string>
<!-- Context: Keyman Settings menu -->
<string name="show_banner_off" comment="Description when toggle is off">عند إيقاف التشغيل، يظهر فقط عند تفعيل النص التنبؤي</string>
<!-- Context: Keyman Settings menu -->
<string name="show_send_crash_report" comment="permission for sending crash reports">السماح بإرسال تقارير توقف التشغيل المفاجئ عبر الشبك</string>
<!-- Context: Keyman Settings menu -->
<string name="show_send_crash_report_on" comment="Description when toggle is on">عند التشغيل، سيتم إرسال تقارير التوقف المفاجئ</string>
<!-- Context: Keyman Settings menu -->
<string name="show_send_crash_report_off" comment="Description when toggle is off">عند إيقاف التشغيل، لن يتم إرسال التقارير</string>
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
<string name="install_from_keyman_dot_com" comment="Install package from Keyman server">تثبيت من keyman.com</string>
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
<string name="install_from_local_file" comment="Install package file from local device">التثبيت من ملف محلي</string>
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
<string name="install_from_other_device" comment="Scan QR Code">التثبيت من جهاز آخر</string>
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
<string name="add_languages_to_installed_keyboard" comment="Install additional languages from a keyboard package">اضف لغات للوحة المفاتيح المثبتة</string>
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
<string name="add_language_subtext" comment="Additional information for adding another language">(من حزمة لوحة المفاتيح)</string>
<!-- Context: Select Package and Select Languages menu -->
<string name="title_select_keyboard_package_list" comment="Select a keyboard package to add languages">اختر حزمة لوحة مفاتيح</string>
<!-- Context: Select Package and Select Languages menu -->
<string name="title_select_languages_for_package" comment="Select language names from the keyboard package">اختر لغة %1$s</string>
<!-- Context: Select Package and Select Languages menu -->
<string name="added_language_to_keyboard" comment="Notification that a language name is added to a keyboard">تمت اضافة %1$s إلى %2$s</string>
<!-- 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>
<!-- Context: Adjust Keyboard Height menu -->
<string name="reset_to_defaults" comment="Button to reset to default heights">إعادة ضبط الافتراضيات</string>
<!-- Context: Keyman Web Browser -->
<string name="hint_text" comment="Prompt to type in the browser search bar">ابحث أو اكتب عنوان URL</string>
<!-- Context: Keyman Web Browser -->
<string name="title_bookmarks" comment="Title for bookmarks">علامات مرجعية</string>
<!-- Context: Keyman Web Browser -->
<string name="no_bookmarks" comment="Text when bookmark list is empty">لا تتوفر أي علامات مرجعية</string>
<!-- Context: Keyman Web Browser -->
<string name="add_bookmark" comment="Confirmation to add bookmark">أضف علامة مرجعية</string>
<!-- Context: Keyman Web Browser -->
<string name="hint_title" comment="Page title for saved bookmark">العنوان</string>
<!-- Context: Keyman Web Browser -->
<string name="hint_url" comment="Page URL for saved bookmark">عنوان Url</string>
<!-- Context: KMP Package strings -->
<string name="title_package_failed_to_install" comment="Error dialog title when package failed to install">فشل في تنزيل حزمة %1$s</string>
<!-- Context: KMP Package strings -->
<string name="downloading_keyboard_package" comment="Confirmation to download keyboard package filename">يتم الآن تحميل لوحة مفاتيح حزمة \n%1$s&#8230;</string>
<!-- Context: KMP Package strings -->
<string name="failed_to_extract" comment="Notification that keyboard package failed to unzip">فشل في الإستخراج</string>
<!-- Context: KMP Package strings -->
<string name="install_keyboard_package" comment="Title to install keyboard package">تثبيت لوحة المفاتيح</string>
<!-- Context: KMP Package strings -->
<string name="install_predictive_text_package" comment="Title to install dictionary package">تثبيت القاموس</string>
<!-- Context: KMP Package strings -->
<string name="not_valid_package_file" comment="Notification when invalid package file cannot be installed">ليس %1$s ملف حزمة صالحة لبرنامج Keyman.\n%2$s\"</string>
<!-- Context: KMP Package strings -->
<string name="no_new_touch_keyboards_to_install" comment="Notification when no touch-optimized keyboards can be installed">ليس في حزمة لوحة المفاتيح لوحات مفاتيح مكيّفة لشاشات اللمس</string>
<!-- Context: KMP Package strings -->
<string name="no_new_predictive_text_to_install" comment="Notification when no dictionaries can be installed">لا يوجد نصوص تنبؤية للتنزيل</string>
<!-- Context: KMP Package strings -->
<string name="no_targets_to_install" comment="Notification when a package doesn't contain keyboards or dictionaries">لا لوحات مفاتيح و نصوص تنبؤية للتنزيل</string>
<!-- Context: KMP Package strings -->
<string name="no_associated_languages" comment="Notification when a keyboard package doesn't contain languages">ليس لحزمة لوحة المفاتيح لغات ذات صلة لتثبيتها</string>
<!-- Context: KMP Package strings -->
<string name="invalid_metadata" comment="kmp.json metadata file is invalid or missing in package">غير صالح\ناقص البيانات الوصفية في الحزمة</string>
<!-- Context: KMP Package strings -->
<string name="minimum_keyboard_version_not_supported" comment="Notification when keyboard has functionality not supported by current Keyman">تتطلب لوحة المفاتيح إصدارًا أحدث من Keyman</string>
<!-- Context: anywhere -->
<string name="unable_to_open_browser" comment="Notification when a browser activity cannot be launched">غير قادر على تشغيل متصفح الويب</string>
</resources>

View file

@ -0,0 +1,164 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- app_name removed in 16.0 beta. Set in build.gradle -->
<!-- Context: Menu Action -->
<string name="action_share" comment="Menu action to send text content to another app">Μοιρασθῆτε</string>
<!-- Context: Menu Action -->
<string name="action_web" comment="Menu action to open Keyman browser">Φυλλομετρητής</string>
<!-- Context: Menu Action -->
<string name="action_text_size" comment="Menu action to adjust text size">Μέγεθος κειμένου</string>
<!-- Context: Menu Action -->
<string name="action_overflow" comment="Show additional menu items">Περισσότερα</string>
<!-- Context: Menu Action -->
<string name="action_clear_text" comment="Menu action to erase text">Διαγραφὴ κειμένου</string>
<!-- Context: Menu Action -->
<string name="action_info" comment="Menu action to display Keyman help page">Πληροφορίες</string>
<!-- Context: Menu Action -->
<string name="action_settings" comment="Menu action to open Settings menu">Ρυθμίσεις</string>
<!-- Context: Menu Action -->
<string name="action_install_updates" comment="Menu notification that keyboard or dictionary updates available">Εγκατάσταση Ενημερώσεων</string>
<!-- Context: Title -->
<string name="title_version" comment="Title of Keyman for Android version">Ἔκδοση %1$s</string>
<!-- Context: Text label when old Chrome version installed -->
<string name="text_require_chrome_57" comment="Banner text when old version of Chrome is installed">Τὸ Keyman ἀπαιτεῖ ἔκδοση Chrome 57 ἢ νεώτερη.</string>
<!-- Context: Button label -->
<string name="button_update_chrome" comment="Prompt to upgrade Chrome in the Play Store">Ἐνημερῶστε τὸ Chrome</string>
<!-- Context: Textfield prompt (&#8230; is the ellipsis character "...") -->
<string name="textview_hint" comment="Prompt to start typing">Ἀρχίστε νὰ γράφετε ἐδῶ&#8230;</string>
<!-- Context: Splash screen (version/copyright info currently disabled -->
<!-- Context: Splash screen (version/copyright info currently disabled -->
<!-- Context: Text Size dialog -->
<string name="text_size" comment="Current text size (number)">Μέγεθος κειμένου: %1$d</string>
<!-- Context: Text Size dialog -->
<string name="ic_text_size_up" comment="Make text bigger">Μεγαλῶστε τὴν γραμματοσειρά</string>
<!-- Context: Text Size dialog -->
<string name="ic_text_size_down" comment="Make text smaller">Μικρύνετε τὴν γραμματοσειρά</string>
<!-- Context: Text Size dialog -->
<string name="ic_text_size_slider" comment="Adjust text size">Ρύθμιση μεγέθους κειμένου</string>
<!-- Context: Clear Text dialog -->
<string name="all_text_will_be_cleared" comment="Erase all the text">\nΤὸ κείμενο θὰ ἐκκαθαρισθεῖ πλήρως\n</string>
<!-- Context: Get Started menu -->
<string name="get_started" comment="Menu for getting started">Μικρύνετε τὸ κείμενο</string>
<!-- Context: Get Started menu -->
<string name="add_a_keyboard" comment="Menu item to add a keyboard">Προσθέστε πληκτρολόγιο γιὰ τὴν γλῶσσα σας</string>
<!-- Context: Get Started menu -->
<string name="enable_system_keyboard" comment="Menu item to enable Keyman as system keyboard">Ὁρίστε τὸ Κῆμαν ὡς παν-συστημικὸ πληκτρολόγιο</string>
<!-- Context: Get Started menu -->
<string name="set_keyman_as_default" comment="Menu item to set Keyman as default system keyboard">Ὁρίστε τὸ Κῆμαν ὡς προεπιλεγμένο πληκτρολόγιο</string>
<!-- Context: Get Started menu -->
<string name="more_info" comment="Open the Keyman for Android help page">Περισσότερες πληροφορίες</string>
<!-- Context: Get Started menu -->
<string name="show_get_started" comment="Show the &quot;Get Started&quot; menu on startup">Κατὰ τὴν ἔναρξη νὰ προβάλλεται τὸ \"%1$s</string>
<!-- Context: Android Storage Permission -->
<string name="request_storage_permission" comment="Request storage permission to access keyboard packages">Γιὰ νὰ ἐγκαταστήσετε πακέτα πληκτρολογίων, ἐπιτρέψτε στὸ Κῆμαν νὰ διαβάζει ἐξωτερικὸ χῶρο ἀποθήκευσης.</string>
<!-- Context: Android Storage Permission -->
<string name="storage_permission_denied" comment="Keyboard package installation may fail since Android storage permission not granted">Ἀπερρίφθη αἴτημα χώρου ἀποθήκευσης. Πιθανὴ ἀποτυχία ἐγκατάστασης πακέτου πληκτρολογίου</string>
<string name="storage_permission_denied2" comment="Keyboard package installation failed. Recommend install from local file">Ἀπερρίφθη αἴτημα χώρου ἀποθηκεύσεως. Δοκιμάστε τὶς ρυθμίσεις Κῆμαν - Ἐγκατάσταση ἀπὸ τοπικὸ ἀρχεῖο</string>
<!-- Context: Keyman Settings menu -->
<string name="keyman_settings" comment="Settings menu title">Ρυθμίσεις</string>
<!-- Context: Keyman Settings menu -->
<plurals name="installed_languages">
<item quantity="one">Ἐγκατεστημένες γλῶσσες (%1$d)</item>
<item quantity="other">Ἐγκατεστημένες γλῶσσες (%1$d)</item>
</plurals>
<!-- Context: Keyman Settings menu -->
<string name="install_keyboard_or_dictionary" comment="Menu item to install keyboard or dictionary">Ἐγκαταστῆστε πληκτρολόγιο ἢ λεξικό</string>
<!-- Context: Keyman Settings menu -->
<string name="change_display_language" comment="Menu action to change interface language">Γλῶσσα προβολῆς</string>
<!-- Context: Keyman Settings menu -->
<string name="adjust_keyboard_height" comment="Menu action to adjust keyboard height">Μεταβολὴ ὕψους πληκτρολογίου</string>
<!-- Context: Keyman Settings menu -->
<string name="spacebar_caption" comment="Menu action to set the spacebar caption">Λεζάντα πλήκτρου διαστήματος</string>
<!-- Context: Keyman Settings menu / Spacebar caption -->
<string name="spacebar_caption_keyboard" comment="Spacebar caption option - keyboard">Πληκτρολόγιο</string>
<!-- Context: Keyman Settings menu / Spacebar caption -->
<string name="spacebar_caption_language" comment="Spacebar caption option - language">Γλῶσσα</string>
<!-- Context: Keyman Settings menu / Spacebar caption -->
<string name="spacebar_caption_language_keyboard" comment="Spacebar caption option - language + keyboard">Γλῶσσα + Πληκτρολόγιο</string>
<!-- Context: Keyman Settings menu / Spacebar caption -->
<string name="spacebar_caption_blank" comment="Spacebar caption option - blank">Κενό</string>
<!-- Context: Keyman Settings menu / Spacebar caption hint -->
<string name="spacebar_caption_hint_keyboard" comment="Spacebar caption hint - keyboard">\'Ονομα πληκτρολογίου στὸ πλῆκτρο διαστήματος</string>
<!-- Context: Keyman Settings menu / Spacebar caption hint -->
<string name="spacebar_caption_hint_language" comment="Spacebar caption hint - language">Ὄνομα γλώσσας στὸ πλῆκτρο διαστήματος</string>
<!-- Context: Keyman Settings menu / Spacebar caption hint -->
<string name="spacebar_caption_hint_language_keyboard" comment="Spacebar caption hint - language + keyboard">Ὄνομα πληκτρολογίου καὶ γλώσσας στὸ πλῆκτρο διαστήματος</string>
<!-- Context: Keyman Settings menu / Spacebar caption hint -->
<string name="spacebar_caption_hint_blank" comment="Spacebar caption hint - blank">Καμμία λεζάντα στὸ πλῆκτρο διαστήματος</string>
<!-- Context: Keyman Settings menu / Haptic feedback -->
<string name="haptic_feedback" comment="haptic feedback on key press">Δὸνηση κατὰ τὴν πληκτρολόγηση</string>
<!-- Context: Keyman Settings menu -->
<string name="show_banner" comment="text suggestions banner">Νὰ ἐμφανίζεται πάντα banner</string>
<!-- Context: Keyman Settings menu -->
<string name="show_banner_on" comment="Description when toggle is on">Πρὸς ὑλοποίησιν</string>
<!-- Context: Keyman Settings menu -->
<string name="show_banner_off" comment="Description when toggle is off">OFF: ἐμφάνιση μόνον ὅταν ἔχει ἐνεργοποιηθεῖ ἡ πρόβλεψη κειμένου</string>
<!-- Context: Keyman Settings menu -->
<string name="show_send_crash_report" comment="permission for sending crash reports">Ἐπιτρέψτε τὴν ἀποστολὴ ἀναφορῶν κατάρρευσης μέσῳ δικτύου</string>
<!-- Context: Keyman Settings menu -->
<string name="show_send_crash_report_on" comment="Description when toggle is on">ON: Ἀποστολὴ ἀναφορῶν κατάρρευσης</string>
<!-- Context: Keyman Settings menu -->
<string name="show_send_crash_report_off" comment="Description when toggle is off">OFF: Δὲν ἀποστέλλονται ἀναφορὲς κατάρρευσης</string>
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
<string name="install_from_keyman_dot_com" comment="Install package from Keyman server">Ἐγκατάσταση ἀπὸ τὸ keyman.com</string>
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
<string name="install_from_local_file" comment="Install package file from local device">Ἐγκατάσταση ἀπὸ τοπικὸ ἀρχεῖο</string>
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
<string name="install_from_other_device" comment="Scan QR Code">Ἐγκατάσταση ἀπὸ ἄλλη συσκευή</string>
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
<string name="add_languages_to_installed_keyboard" comment="Install additional languages from a keyboard package">Προσθέστε γλῶσσες σὲ ἐγκατεστημένο πληκτρολόγιο</string>
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
<string name="add_language_subtext" comment="Additional information for adding another language">(ἀπὸ πακέτο πληκτρολογίου)</string>
<!-- Context: Select Package and Select Languages menu -->
<string name="title_select_keyboard_package_list" comment="Select a keyboard package to add languages">Ἐπιλέξτε Πακέτο Πληκτρολογίου</string>
<!-- Context: Select Package and Select Languages menu -->
<string name="title_select_languages_for_package" comment="Select language names from the keyboard package">Ἐπιλέξτε γλῶσσες γιὰ τὸ %1$s</string>
<!-- Context: Select Package and Select Languages menu -->
<string name="added_language_to_keyboard" comment="Notification that a language name is added to a keyboard">Προσετέθη ἡ γλῶσσα %1$s στὸ %2$s</string>
<!-- 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>
<!-- Context: Adjust Keyboard Height menu -->
<string name="reset_to_defaults" comment="Button to reset to default heights">Ἐπαναφέρετε τὶς προεπιλεγμένες ρυθμίσεις</string>
<!-- Context: Keyman Web Browser -->
<string name="hint_text" comment="Prompt to type in the browser search bar">Ἀναζητῆστε ἢ πληκτρολογῆστε URL</string>
<!-- Context: Keyman Web Browser -->
<string name="title_bookmarks" comment="Title for bookmarks">Σελιδοδεῖκτες</string>
<!-- Context: Keyman Web Browser -->
<string name="no_bookmarks" comment="Text when bookmark list is empty">Δὲν ὑπάρχουν σελιδοδεῖκτες</string>
<!-- Context: Keyman Web Browser -->
<string name="add_bookmark" comment="Confirmation to add bookmark">Προσθέστε σελιδοδείκτη</string>
<!-- Context: Keyman Web Browser -->
<string name="hint_title" comment="Page title for saved bookmark">Τίτλος</string>
<!-- Context: Keyman Web Browser -->
<string name="hint_url" comment="Page URL for saved bookmark">URL</string>
<!-- Context: KMP Package strings -->
<string name="title_package_failed_to_install" comment="Error dialog title when package failed to install">Τὸ πακέτο %1$s ἀπέτυχε νὰ ἐγκατασταθεῖ</string>
<!-- Context: KMP Package strings -->
<string name="downloading_keyboard_package" comment="Confirmation to download keyboard package filename">Λήψη πακέτου πληκτρολογίου\n%1$s&#8230;</string>
<!-- Context: KMP Package strings -->
<string name="failed_to_extract" comment="Notification that keyboard package failed to unzip">Ἀποτυχία ἐξαγωγῆς</string>
<!-- Context: KMP Package strings -->
<string name="install_keyboard_package" comment="Title to install keyboard package">Ἐγκαταστῆστε πληκτρολόγιο</string>
<!-- Context: KMP Package strings -->
<string name="install_predictive_text_package" comment="Title to install dictionary package">Ἐγκαταστῆστε Λεξικό</string>
<!-- Context: KMP Package strings -->
<string name="not_valid_package_file" comment="Notification when invalid package file cannot be installed">Τὸ %1$s δὲν εἶναι ἔγκυρο ἀρχεῖο πακέτου Κῆμαν.\n%2$s\"</string>
<!-- Context: KMP Package strings -->
<string name="no_new_touch_keyboards_to_install" comment="Notification when no touch-optimized keyboards can be installed">Τὸ πακέτο πληκτρολογίου δὲν ἔχει βελτιστοποιημένα πληκτρολόγια ἀφῆς πρὸς ἐγκατάστασιν</string>
<!-- Context: KMP Package strings -->
<string name="no_new_predictive_text_to_install" comment="Notification when no dictionaries can be installed">Δὲν ὑπάρχουν προγνώσεις κειμένων πρὸς ἐγκατάστασιν</string>
<!-- Context: KMP Package strings -->
<string name="no_targets_to_install" comment="Notification when a package doesn't contain keyboards or dictionaries">Δὲν ὑπάρχουν πληκτρολόγια ἢ προγνώσεις κειμένων πρὸς ἐγκατάστασιν</string>
<!-- Context: KMP Package strings -->
<string name="no_associated_languages" comment="Notification when a keyboard package doesn't contain languages">Τὸ πακέτο πληκτρολογίου δὲν ἔχει σχετικὲς μὲ αὐτὸ γλῶσσες πρὸς ἐγκατάστασιν</string>
<!-- Context: KMP Package strings -->
<string name="invalid_metadata" comment="kmp.json metadata file is invalid or missing in package">Ἄκυρα/ἐλλιπῆ μεταδεδομένα στὸ πακέτο</string>
<!-- Context: KMP Package strings -->
<string name="minimum_keyboard_version_not_supported" comment="Notification when keyboard has functionality not supported by current Keyman"> Τὸ πληκτρολόγιο ἀπαιτεῖ νεώτερη ἔκδοση τοῦ Κῆμαν</string>
<!-- Context: anywhere -->
<string name="unable_to_open_browser" comment="Notification when a browser activity cannot be launched">Ἀδυναμία ἐκκινήσεως φυλλομετρητῆ</string>
</resources>

View file

@ -33,6 +33,8 @@
<string name="ic_text_size_up" comment="Make text bigger">Velikost textu nahoru</string>
<!-- Context: Text Size dialog -->
<string name="ic_text_size_down" comment="Make text smaller">Velikost textu dolů</string>
<!-- Context: Text Size dialog -->
<string name="ic_text_size_slider" comment="Adjust text size">Posuvník velikosti textu</string>
<!-- Context: Clear Text dialog -->
<string name="all_text_will_be_cleared" comment="Erase all the text">\nVeškerý text bude vymazán\n</string>
<!-- Context: Get Started menu -->
@ -51,6 +53,7 @@
<string name="request_storage_permission" comment="Request storage permission to access keyboard packages"> Pro instalaci balíčků klávesnice povolte klávesnici oprávnění ke čtení externího úložiště.</string>
<!-- Context: Android Storage Permission -->
<string name="storage_permission_denied" comment="Keyboard package installation may fail since Android storage permission not granted"> Žádost o oprávnění k úložišti byla zamítnuta. Může selhat instalace balíčku klávesnice</string>
<string name="storage_permission_denied2" comment="Keyboard package installation failed. Recommend install from local file"> Žádost o oprávnění k úložišti se nezdařila. Zkuste nastavení Keyman - Instalovat z místního souboru</string>
<!-- Context: Keyman Settings menu -->
<string name="keyman_settings" comment="Settings menu title">Nastavení</string>
<!-- Context: Keyman Settings menu -->
@ -67,6 +70,8 @@
<!-- Context: Keyman Settings menu -->
<string name="adjust_keyboard_height" comment="Menu action to adjust keyboard height">Upravit výšku klávesnice</string>
<!-- Context: Keyman Settings menu -->
<string name="adjust_longpress_delay" comment="Menu action to adjust longpress delay duration">Upravit zpoždění dlouhého stisknutí</string>
<!-- Context: Keyman Settings menu -->
<string name="spacebar_caption" comment="Menu action to set the spacebar caption">Titulek mezerníku</string>
<!-- Context: Keyman Settings menu / Spacebar caption -->
<string name="spacebar_caption_keyboard" comment="Spacebar caption option - keyboard">Keyboard</string>
@ -85,7 +90,7 @@
<!-- Context: Keyman Settings menu / Spacebar caption hint -->
<string name="spacebar_caption_hint_blank" comment="Spacebar caption hint - blank">Nezobrazovat titulek v mezerníku</string>
<!-- Context: Keyman Settings menu / Haptic feedback -->
<string name="haptic_feedback" comment="haptic feedback on key press">Vibrate when typing</string>
<string name="haptic_feedback" comment="haptic feedback on key press">Při psaní vibrovat</string>
<!-- Context: Keyman Settings menu -->
<string name="show_banner" comment="text suggestions banner">Vždy zobrazovat banner</string>
<!-- Context: Keyman Settings menu -->
@ -122,6 +127,14 @@
<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>
<!-- 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 -->
<string name="longpress_delay_time" comment="Current longpress delay time (seconds)">Doba zpoždění: %1$.1f sekund</string>
<!-- Context: Adjust Longpress Delay Time menu -->
<string name="ic_delay_time_up" comment="Make longpress delay longer">Prodloužit dobu zpoždění</string>
<!-- Context: Adjust Longpress Delay Time menu -->
<string name="ic_delay_time_down" comment="Make longpress delay shorter">Zkrátit dobu zpoždění</string>
<!-- Context: Adjust Longpress Delay Time menu -->
<string name="ic_longpress_delay_slider" comment="Adjust longpress delay time">Posuvník doby zpoždění dlouhého stisknutí</string>
<!-- Context: Keyman Web Browser -->
<string name="hint_text" comment="Prompt to type in the browser search bar">Hledat nebo zadat URL</string>
<!-- Context: Keyman Web Browser -->
@ -157,7 +170,7 @@
<!-- Context: KMP Package strings -->
<string name="invalid_metadata" comment="kmp.json metadata file is invalid or missing in package">Neplatná/chybějící metadata v balíčku</string>
<!-- Context: KMP Package strings -->
<string name="minimum_keyboard_version_not_supported" comment="Notification when keyboard has functionality not supported by current Keyman"> Keyboard requires a newer version of Keyman</string>
<string name="minimum_keyboard_version_not_supported" comment="Notification when keyboard has functionality not supported by current Keyman"> Klávesnice vyžaduje novější verzi Keyman</string>
<!-- Context: anywhere -->
<string name="unable_to_open_browser" comment="Notification when a browser activity cannot be launched">Unable to launch web browser</string>
<string name="unable_to_open_browser" comment="Notification when a browser activity cannot be launched">Nelze spustit webový prohlížeč</string>
</resources>

View file

@ -34,7 +34,7 @@
<!-- Context: Text Size dialog -->
<string name="ic_text_size_down" comment="Make text smaller">Text kleiner</string>
<!-- Context: Text Size dialog -->
<string name="ic_text_size_slider" comment="Adjust text size">Text size slider</string>
<string name="ic_text_size_slider" comment="Adjust text size">Slider für Textgröße</string>
<!-- Context: Clear Text dialog -->
<string name="all_text_will_be_cleared" comment="Erase all the text">\nDer gesamte Text wird gelöscht\n</string>
<!-- Context: Get Started menu -->
@ -53,6 +53,7 @@
<string name="request_storage_permission" comment="Request storage permission to access keyboard packages"> Um Tastaturpakete zu installieren, braucht Keyman die Berechtigung zum Lesen von externem Speicher.</string>
<!-- Context: Android Storage Permission -->
<string name="storage_permission_denied" comment="Keyboard package installation may fail since Android storage permission not granted"> Zugriff auf den Speicher wurde abgelehnt. Möglicherweise funktioniert die Installation des Tastaturpakets nicht korrekt</string>
<string name="storage_permission_denied2" comment="Keyboard package installation failed. Recommend install from local file"> Speicherberechtigungsanfrage fehlgeschlagen. Versuchen Sie die Keyman Einstellungen - Aus der lokalen Datei installieren</string>
<!-- Context: Keyman Settings menu -->
<string name="keyman_settings" comment="Settings menu title">Einstellungen</string>
<!-- Context: Keyman Settings menu -->
@ -63,7 +64,7 @@
<!-- Context: Keyman Settings menu -->
<string name="install_keyboard_or_dictionary" comment="Menu item to install keyboard or dictionary">Tastatur oder Wörterbuch installieren</string>
<!-- Context: Keyman Settings menu -->
<string name="change_display_language" comment="Menu action to change interface language">Sprache anzeigen</string>
<string name="change_display_language" comment="Menu action to change interface language">Anzeigesprache</string>
<!-- Context: Keyman Settings menu -->
<string name="adjust_keyboard_height" comment="Menu action to adjust keyboard height">Tastaturhöhe anpassen</string>
<!-- Context: Keyman Settings menu -->

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- app_name removed in 16.0 beta. Set in build.gradle -->
<!-- Context: Menu Action -->
<string name="action_share" comment="Menu action to send text content to another app">Condividi</string>
<!-- Context: Menu Action -->
@ -32,6 +33,8 @@
<string name="ic_text_size_up" comment="Make text bigger">Dimensione testo in alto</string>
<!-- Context: Text Size dialog -->
<string name="ic_text_size_down" comment="Make text smaller">Dimensione del testo giù</string>
<!-- Context: Text Size dialog -->
<string name="ic_text_size_slider" comment="Adjust text size">Cursore dimensione testo</string>
<!-- Context: Clear Text dialog -->
<string name="all_text_will_be_cleared" comment="Erase all the text">\nTutto il testo sarà cancellato\n</string>
<!-- Context: Get Started menu -->
@ -50,6 +53,7 @@
<string name="request_storage_permission" comment="Request storage permission to access keyboard packages"> Per installare pacchetti di tastiera, consentire a Keyman di leggere la memoria esterna.</string>
<!-- Context: Android Storage Permission -->
<string name="storage_permission_denied" comment="Keyboard package installation may fail since Android storage permission not granted"> La richiesta di autorizzazione all\'archiviazione è stata negata. Potrebbe non essere stato possibile installare il pacchetto tastiera</string>
<string name="storage_permission_denied2" comment="Keyboard package installation failed. Recommend install from local file"> Richiesta di autorizzazione archiviazione non riuscita. Prova Impostazioni Keyman - Installa da file locale</string>
<!-- Context: Keyman Settings menu -->
<string name="keyman_settings" comment="Settings menu title">Impostazioni</string>
<!-- Context: Keyman Settings menu -->
@ -64,6 +68,8 @@
<!-- Context: Keyman Settings menu -->
<string name="adjust_keyboard_height" comment="Menu action to adjust keyboard height">Regola altezza tastiera</string>
<!-- Context: Keyman Settings menu -->
<string name="adjust_longpress_delay" comment="Menu action to adjust longpress delay duration">Adjust longpress delay</string>
<!-- Context: Keyman Settings menu -->
<string name="spacebar_caption" comment="Menu action to set the spacebar caption">Didascalia barra spaziatrice</string>
<!-- Context: Keyman Settings menu / Spacebar caption -->
<string name="spacebar_caption_keyboard" comment="Spacebar caption option - keyboard">Keyboard</string>
@ -82,7 +88,7 @@
<!-- 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 / Haptic feedback -->
<string name="haptic_feedback" comment="haptic feedback on key press">Vibrate when typing</string>
<string name="haptic_feedback" comment="haptic feedback on key press">Vibra durante la digitazione</string>
<!-- Context: Keyman Settings menu -->
<string name="show_banner" comment="text suggestions banner">Mostra sempre il banner</string>
<!-- Context: Keyman Settings menu -->
@ -119,6 +125,14 @@
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">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: Adjust Longpress Delay Time menu -->
<string name="longpress_delay_time" comment="Current longpress delay time (seconds)">Delay Time: %1$.1f seconds</string>
<!-- Context: Adjust Longpress Delay Time menu -->
<string name="ic_delay_time_up" comment="Make longpress delay longer">Delay time longer</string>
<!-- Context: Adjust Longpress Delay Time menu -->
<string name="ic_delay_time_down" comment="Make longpress delay shorter">Delay time shorter</string>
<!-- Context: Adjust Longpress Delay Time menu -->
<string name="ic_longpress_delay_slider" comment="Adjust longpress delay time">Longpress delay time slider</string>
<!-- Context: Keyman Web Browser -->
<string name="hint_text" comment="Prompt to type in the browser search bar">Cerca o digita URL</string>
<!-- Context: Keyman Web Browser -->
@ -154,7 +168,7 @@
<!-- Context: KMP Package strings -->
<string name="invalid_metadata" comment="kmp.json metadata file is invalid or missing in package">Metadati non validi/mancanti nel pacchetto</string>
<!-- Context: KMP Package strings -->
<string name="minimum_keyboard_version_not_supported" comment="Notification when keyboard has functionality not supported by current Keyman"> Keyboard requires a newer version of Keyman</string>
<string name="minimum_keyboard_version_not_supported" comment="Notification when keyboard has functionality not supported by current Keyman"> La tastiera richiede una nuova versione di Keyman</string>
<!-- Context: anywhere -->
<string name="unable_to_open_browser" comment="Notification when a browser activity cannot be launched">Unable to launch web browser</string>
<string name="unable_to_open_browser" comment="Notification when a browser activity cannot be launched">Impossibile avviare il browser web</string>
</resources>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- app_name removed in 16.0 beta. Set in build.gradle -->
<!-- Context: Menu Action -->
<string name="action_share" comment="Menu action to send text content to another app">Compartilhar</string>
<!-- Context: Menu Action -->
@ -32,6 +33,8 @@
<string name="ic_text_size_up" comment="Make text bigger">Tamanho do texto para cima</string>
<!-- Context: Text Size dialog -->
<string name="ic_text_size_down" comment="Make text smaller">Tamanho do texto para baixo</string>
<!-- Context: Text Size dialog -->
<string name="ic_text_size_slider" comment="Adjust text size">Tamanho do texto deslizante</string>
<!-- Context: Clear Text dialog -->
<string name="all_text_will_be_cleared" comment="Erase all the text">\nTodo o texto será limpo\n</string>
<!-- Context: Get Started menu -->
@ -50,6 +53,7 @@
<string name="request_storage_permission" comment="Request storage permission to access keyboard packages"> Para instalar pacotes de teclado, permita que o Keyman leia o armazenamento externo.</string>
<!-- Context: Android Storage Permission -->
<string name="storage_permission_denied" comment="Keyboard package installation may fail since Android storage permission not granted"> A solicitação de permissão de armazenamento foi negada. Pode falhar na instalação do pacote de teclado</string>
<string name="storage_permission_denied2" comment="Keyboard package installation failed. Recommend install from local file"> Falha no pedido de permissão de armazenamento. Experimente instalar de um ficheiro local, nas configurações do Keyman</string>
<!-- Context: Keyman Settings menu -->
<string name="keyman_settings" comment="Settings menu title">Configurações</string>
<!-- Context: Keyman Settings menu -->
@ -82,7 +86,7 @@
<!-- 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 / Haptic feedback -->
<string name="haptic_feedback" comment="haptic feedback on key press">Vibrate when typing</string>
<string name="haptic_feedback" comment="haptic feedback on key press">Vibrar ao tocar</string>
<!-- Context: Keyman Settings menu -->
<string name="show_banner" comment="text suggestions banner">Sempre mostrar banner</string>
<!-- Context: Keyman Settings menu -->
@ -154,7 +158,7 @@
<!-- Context: KMP Package strings -->
<string name="invalid_metadata" comment="kmp.json metadata file is invalid or missing in package">Metadados inválidos/ausentes no pacote</string>
<!-- Context: KMP Package strings -->
<string name="minimum_keyboard_version_not_supported" comment="Notification when keyboard has functionality not supported by current Keyman"> Keyboard requires a newer version of Keyman</string>
<string name="minimum_keyboard_version_not_supported" comment="Notification when keyboard has functionality not supported by current Keyman">O teclado requer uma nova versão do Keyman</string>
<!-- Context: anywhere -->
<string name="unable_to_open_browser" comment="Notification when a browser activity cannot be launched">Unable to launch web browser</string>
<string name="unable_to_open_browser" comment="Notification when a browser activity cannot be launched">Não foi possível carregar o navegador</string>
</resources>

View file

@ -23,4 +23,5 @@
<dimen name="preference_icon">32dp</dimen>
<dimen name="update_indicator_size">16dp</dimen>
<dimen name="update_indicator_bg_margin">0dp</dimen>
<dimen name="longpress_delay_label_textsize">24sp</dimen>
</resources>

View file

@ -113,6 +113,9 @@
<!-- Context: Keyman Settings menu -->
<string name="adjust_keyboard_height" comment="Menu action to adjust keyboard height">Adjust keyboard height</string>
<!-- Context: Keyman Settings menu -->
<string name="adjust_longpress_delay" comment="Menu action to adjust longpress delay duration">Adjust longpress delay</string>
<!-- Context: Keyman Settings menu -->
<string name="spacebar_caption" comment="Menu action to set the spacebar caption">Spacebar caption</string>
@ -198,6 +201,17 @@
<!-- Context: Adjust Keyboard Height menu -->
<string name="reset_to_defaults" comment="Button to reset to default heights">Reset to Defaults</string>
<!-- Context: Adjust Longpress Delay Time menu -->
<string name="longpress_delay_time" comment="Current longpress delay time (seconds)">Delay Time: %1$.1f seconds</string>
<!-- Context: Adjust Longpress Delay Time menu -->
<string name="ic_delay_time_up" comment="Make longpress delay longer">Delay time longer</string>
<!-- Context: Adjust Longpress Delay Time menu -->
<string name="ic_delay_time_down" comment="Make longpress delay shorter">Delay time shorter</string>
<!-- Context: Adjust Longpress Delay Time menu -->
<string name="ic_longpress_delay_slider" comment="Adjust longpress delay time">Longpress delay time slider</string>
<!-- Context: Keyman Web Browser -->
<string name="hint_text" comment="Prompt to type in the browser search bar">Search or type URL</string>

View file

@ -55,17 +55,17 @@ android {
dependencies {
// 1.6.0-rc01 needed to resolve https://issuetracker.google.com/issues/238425626
implementation 'androidx.appcompat:appcompat:1.6.0-rc01' // 1.5.1
implementation 'androidx.appcompat:appcompat:1.6.1'
// material:1.7.0 will need Gradle plugin 7.1.0+
implementation 'com.google.android.material:material:1.6.0'
implementation 'commons-io:commons-io:2.6'
implementation 'io.sentry:sentry-android:6.9.2'
implementation 'androidx.preference:preference:1.2.0'
implementation 'com.google.android.material:material:1.12.0'
implementation 'commons-io:commons-io:2.13.0'
implementation 'io.sentry:sentry-android:7.8.0'
implementation 'androidx.preference:preference:1.2.1'
// Robolectric
testImplementation 'androidx.test:core:1.5.0'
testImplementation 'androidx.test.ext:junit:1.1.4'
testImplementation 'org.robolectric:robolectric:4.8.1'
testImplementation 'androidx.test.ext:junit:1.1.5'
testImplementation 'org.robolectric:robolectric:4.10.3'
// Generate QR Codes
implementation ('com.github.kenglxn.QRGen:android:3.0.1') {

View file

@ -12,6 +12,7 @@ var bannerHeight = 0;
var bannerImagePath = '';
var bannerHTMLContents = '';
var fragmentToggle = 0;
var deferredBannerCall;
var sentryManager = new KeymanSentryManager({
hostPlatform: "android"
@ -49,6 +50,12 @@ function init() {
// The OSK is not available until initialization is complete.
keyman.osk.bannerView.activeBannerHeight = bannerHeight;
if(deferredBannerCall) {
deferredBannerCall();
deferredBannerCall = null;
}
keyman.refreshOskLayout();
}
});
@ -63,10 +70,19 @@ function init() {
}
function showBanner(flag) {
if(!keyman.osk) {
deferredBannerCall = function() {
showBanner(flag);
}
return;
}
var bc = keyman.osk.bannerController;
console_debug("Setting banner display for dictionaryless keyboards to " + flag);
console_debug("bannerHTMLContents: " + bannerHTMLContents);
var bc = keyman.osk.bannerController;
if (bc) {
if(bc) {
if (bannerHTMLContents != '') {
bc.inactiveBanner = flag ? new bc.HTMLBanner(bannerHTMLContents) : null;
} else {
@ -96,6 +112,17 @@ function notifyHost(event, params) {
}, 10);
}
// Update the KeymanWeb longpress delay
// delay is in milliseconds
function setLongpressDelay(delay) {
if (keyman.osk) {
keyman.osk.gestureParams.longpress.waitLength = delay;
console_debug('setLongpressDelay('+delay+')');
} else {
window.console.log('setLongpressDelay error: keyman.osk undefined');
}
}
// Update the KMW banner height
// h is in dpi (different from iOS)
function setBannerHeight(h) {
@ -125,7 +152,7 @@ function setOskHeight(h) {
function setOskWidth(w) {
if(w > 0) {
oskWidth = w;
oskWidth = w / window.devicePixelRatio;
}
}
@ -172,7 +199,8 @@ function setKeymanLanguage(k) {
}
function setSpacebarText(mode) {
keyman.config.spacebarText = mode;
var text = (mode == undefined) || !mode.text ? '' : mode.text;
keyman.config.spacebarText = text;
}
// #6665: we need to know when the user has pressed a hardware key so we don't
@ -230,10 +258,8 @@ function setNumericLayer() {
}
}
function updateKMText(text) {
if(text == undefined) {
text = '';
}
function updateKMText(k) {
var text = (k == undefined) || !k.text ? '' : k.text;
console_debug('updateKMText(text=' + text + ') with: \n' + build_context_string(keyman.context));
@ -278,7 +304,7 @@ function updateKMSelectionRange(start, end) {
console_debug('result:\n' + build_context_string(context));
} else {
console.debug('range unchanged');
console_debug('range unchanged');
}
}
@ -318,6 +344,14 @@ function menuKeyUp() {
window.location.hash = hash;
}
// The keyboard-picker displayed via Android longpress disrupts Web-side
// gesture-handling; this function helps force-clear the globe key's highlighting.
function clearGlobeHighlight() {
if(keyman.osk && keyman.osk.vkbd && keyman.osk.vkbd.currentLayer.globeKey) {
keyman.osk.vkbd.currentLayer.globeKey.highlight(false)
}
}
function hideKeyboard() {
fragmentToggle = (fragmentToggle + 1) % 100;
window.location.hash = 'hideKeyboard' + fragmentToggle;

View file

@ -1,30 +0,0 @@
<!DOCTYPE html>
<html>
<!--
Copyright (C) 2017-2019 SIL International. All rights reserved.
-->
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no">
<title>Keyman</title>
<!-- Provides polyfills needed by un-updated Chromium versions found
on older, but still-supported, Android APIs.
If and when our minimum supported Android version shipped with
Chromium version 54 or greater, we should be able to drop this
polyfill.
-->
<script src="es6-shim.min.js"></script>
<script src="other-polyfills.js"></script>
<script src="map-polyfill.js"></script>
<script src="keymanweb-webview.es5.js"></script>
<script src="sentry.min.js"></script>
<script src="keyman-sentry.js"></script>
<script src="android-host.js"></script>
<style type="text/css">
body {background-color:#000000;}
</style>
</head>
<body class="kmw-embedded keyman-app">
</body>
</html>

View file

@ -7,6 +7,8 @@
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no">
<title>Keyman</title>
<!-- DOMRect only exists as of Chrome 61... thus coming later than ES6. -->
<script src="other-polyfills.js"></script>
<script src="keymanweb-webview.js"></script>
<script src="sentry.min.js"></script>
<script src="keyman-sentry.js"></script>

View file

@ -40,6 +40,7 @@ public class DisplayLanguages {
DisplayLanguageType[] languages = {
new DisplayLanguageType(unspecifiedLocale, context.getString(R.string.default_locale)),
new DisplayLanguageType("am-ET", "አማርኛ (Amharic)"),
new DisplayLanguageType("ar-SA", "(Arabic) العربية"),
new DisplayLanguageType("az-AZ", "Azərbaycanca (Azəricə)"),
new DisplayLanguageType("bwr-NG", "Bura-Pabir"),
new DisplayLanguageType("cs-CZ", "čeština (Czech)"),
@ -62,6 +63,7 @@ public class DisplayLanguages {
new DisplayLanguageType("nl-NL", "Nederlands (Dutch)"),
new DisplayLanguageType("ann", "Obolo"),
new DisplayLanguageType("pl-PL", "Polski (Polish)"),
new DisplayLanguageType("el", "Polytonic Greek"),
new DisplayLanguageType("pt-PT", "Português do Portugal"),
new DisplayLanguageType("ff-ZA", "Pulaar-Fulfulde"), // or Fulah
new DisplayLanguageType("ru-RU", "Pyccĸий (Russian)"),

View file

@ -29,6 +29,7 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Handler;
import android.util.DisplayMetrics;
import android.util.Log;
@ -149,13 +150,19 @@ final class KMKeyboard extends WebView {
protected boolean updateText(String text) {
boolean result = false;
JSONObject reg = new JSONObject();
String kmText = "";
if (text != null) {
kmText = text.toString().replace("\\", "\\u005C").replace("'", "\\u0027").replace("\n", "\\n");
// Use JSON to handle passing string to Javascript
try {
reg.put("text", text.toString());
} catch (JSONException e) {
KMLog.LogException(TAG, "", e);
}
}
if (KMManager.isKeyboardLoaded(this.keyboardType) && !shouldIgnoreTextChange) {
this.loadJavascript(KMString.format("updateKMText('%s')", kmText));
this.loadJavascript(KMString.format("updateKMText(%s)", reg.toString()));
result = true;
}
@ -163,41 +170,69 @@ final class KMKeyboard extends WebView {
return result;
}
protected boolean updateSelectionRange(int selStart, int selEnd) {
boolean result = false;
/**
* Updates the selection range of the current context.
* Returns boolean - true if the selection range was updated successfully
*/
protected boolean updateSelectionRange() {
InputConnection ic = KMManager.getInputConnection(this.keyboardType);
if (ic != null) {
ExtractedText icText = ic.getExtractedText(new ExtractedTextRequest(), 0);
if (icText == null) {
return false;
}
String rawText = icText.text.toString();
updateText(rawText.toString());
/*
The values of selStart & selEnd provided by the system are in code units,
not code-points. We need to account for surrogate pairs here.
Fortunately, it uses UCS-2 encoding... just like JS.
References:
- https://stackoverflow.com/a/23980211
- https://android.googlesource.com/platform/frameworks/base/+/152944f/core/java/android/view/inputmethod/InputConnection.java#326
*/
// Count the number of characters which are surrogate pairs.
int pairsAtStart = CharSequenceUtil.countSurrogatePairs(rawText.substring(0, selStart), rawText.length());
String selectedText = rawText.substring(selStart, selEnd);
int pairsSelected = CharSequenceUtil.countSurrogatePairs(selectedText, selectedText.length());
selStart -= pairsAtStart;
selEnd -= (pairsAtStart + pairsSelected);
if (ic == null) {
// Unable to get connection to the text
return false;
}
this.loadJavascript(KMString.format("updateKMSelectionRange(%d,%d)", selStart, selEnd));
result = true;
return result;
ExtractedText icText = ic.getExtractedText(new ExtractedTextRequest(), 0);
if (icText == null) {
// Failed to get text becausee either input connection became invalid or client is taking too long to respond
// https://developer.android.com/reference/android/view/inputmethod/InputConnection#getExtractedText(android.view.inputmethod.ExtractedTextRequest,%20int)
return false;
}
String rawText = icText.text.toString();
updateText(rawText.toString());
int selMin = icText.selectionStart, selMax = icText.selectionEnd;
int textLength = rawText.length();
if (selMin < 0 || selMax < 0) {
// There is no selection or cursor
// Reference https://developer.android.com/reference/android/text/Selection#getSelectionEnd(java.lang.CharSequence)
return false;
} else if (selMin > textLength || selMax > textLength) {
// Selection is past end of existing text -- should not be possible but we
// are seeing it happen; #11506
return false;
}
if (selMin > selMax) {
// Selection is reversed so "swap"
selMin = icText.selectionEnd;
selMax = icText.selectionStart;
}
/*
The values of selStart & selEnd provided by the system are in code units,
not code-points. We need to account for surrogate pairs here.
Fortunately, it uses UCS-2 encoding... just like JS.
References:
- https://stackoverflow.com/a/23980211
- https://android.googlesource.com/platform/frameworks/base/+/152944f/core/java/android/view/inputmethod/InputConnection.java#326
*/
// Count the number of characters which are surrogate pairs.
int pairsAtStart = CharSequenceUtil.countSurrogatePairs(rawText.substring(0, selMin), rawText.length());
String selectedText = rawText.substring(selMin, selMax);
int pairsSelected = CharSequenceUtil.countSurrogatePairs(selectedText, selectedText.length());
selMin -= pairsAtStart;
selMax -= (pairsAtStart + pairsSelected);
this.loadJavascript(KMString.format("updateKMSelectionRange(%d,%d)", selMin, selMax));
return true;
}
@ -219,10 +254,17 @@ final class KMKeyboard extends WebView {
getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
getSettings().setSupportZoom(false);
getSettings().setTextZoom(100);
getSettings().setUseWideViewPort(true);
getSettings().setLoadWithOverviewMode(true);
if (0 != (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE)) {
// When `.isTestMode() == true`, the setWebContentsDebuggingEnabled method is not available
// and thus will trigger unit-test failures.
if (!KMManager.isTestMode() && (
(context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0 ||
KMManager.getTier(null) != KMManager.Tier.STABLE
)) {
// Enable debugging of WebView via adb. Not used during unit tests
// Refer: https://developer.chrome.com/docs/devtools/remote-debugging/webviews/#configure_webviews_for_debugging
setWebContentsDebuggingEnabled(true);
@ -271,6 +313,12 @@ final class KMKeyboard extends WebView {
public void onLongPress(MotionEvent event) {
if (KMManager.getGlobeKeyState() == KMManager.GlobeKeyState.GLOBE_KEY_STATE_DOWN) {
KMManager.setGlobeKeyState(KMManager.GlobeKeyState.GLOBE_KEY_STATE_LONGPRESS);
// When we activate the keyboard picker, this will disrupt the JS-side's control
// flow for gesture-handling; we should pre-emptively clear the globe key,
// as Web will not receive a "globe key up" event.
loadJavascript("clearGlobeHighlight()");
KMManager.handleGlobeKeyAction(context, true, keyboardType);
return;
/* For future implementation
@ -334,7 +382,8 @@ final class KMKeyboard extends WebView {
allCalls.append(";");
}
loadUrl("javascript:" + allCalls.toString());
// Ensure strings safe for Javascript. TODO: font strings
loadUrl("javascript:" + Uri.encode(allCalls.toString()));
if(javascriptAfterLoad.size() > 0 && keyboardSet) {
callJavascriptAfterLoad();
@ -400,20 +449,15 @@ final class KMKeyboard extends WebView {
dismissHelpBubble();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
RelativeLayout.LayoutParams params = KMManager.getKeyboardLayoutParams();
// I suspect this is the part we should actually be calling directly...
this.setLayoutParams(params);
int bannerHeight = KMManager.getBannerHeight(context);
int oskHeight = KMManager.getKeyboardHeight(context);
if (this.htmlBannerString != null && !this.htmlBannerString.isEmpty()) {
setHTMLBanner(this.htmlBannerString);
}
loadJavascript(KMString.format("setBannerHeight(%d)", bannerHeight));
loadJavascript(KMString.format("setOskWidth(%d)", newConfig.screenWidthDp));
loadJavascript(KMString.format("setOskHeight(%d)", oskHeight));
this.invalidate();
this.requestLayout();
this.dismissHelpBubble();
@ -422,6 +466,27 @@ final class KMKeyboard extends WebView {
}
}
@Override
public void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
super.onSizeChanged(width, height, oldWidth, oldHeight);
int bannerHeight = KMManager.getBannerHeight(context);
int oskHeight = KMManager.getKeyboardHeight(context);
if(bannerHeight + oskHeight != height) {
// We'll proceed, but cautiously and with logging.
KMLog.LogInfo(TAG, "Height mismatch: onSizeChanged = " + height + ", our version = " + (bannerHeight + oskHeight));
}
if (this.htmlBannerString != null && !this.htmlBannerString.isEmpty()) {
setHTMLBanner(this.htmlBannerString);
}
loadJavascript(KMString.format("setBannerHeight(%d)", bannerHeight));
loadJavascript(KMString.format("setOskWidth(%d)", width));
// Must be last - it's the one that triggers a Web-engine layout refresh.
loadJavascript(KMString.format("setOskHeight(%d)", oskHeight));
}
public void dismissSuggestionMenuWindow() {
try {
if (suggestionMenuWindow != null && suggestionMenuWindow.isShowing()) {
@ -870,10 +935,7 @@ final class KMKeyboard extends WebView {
rotateSuggestions.setClickable(false);
// Compute the actual display position (offset coordinate by actual screen pos of kbd)
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(metrics);
float density = metrics.density;
float density = KMManager.getWindowDensity(context);
int posX, posY;
if (keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP) {
@ -1082,8 +1144,17 @@ final class KMKeyboard extends WebView {
}
public void setSpacebarText(KMManager.SpacebarText mode) {
String jsString = KMString.format("setSpacebarText('%s')", mode.toString());
loadJavascript(jsString);
JSONObject reg = new JSONObject();
if (mode != null) {
// Use JSON to handle passing string to Javascript
try {
reg.put("text", mode.toString());
} catch (JSONException e) {
KMLog.LogException(TAG, "", e);
}
}
this.loadJavascript(KMString.format("setSpacebarText(%s)", reg.toString()));
}
/* Implement handleTouchEvent to catch long press gesture without using Android system default time

View file

@ -20,6 +20,7 @@ import android.webkit.JavascriptInterface;
import static android.content.Context.VIBRATOR_SERVICE;
import com.keyman.engine.KMManager.EnterModeType;
import com.keyman.engine.KMManager.KeyboardType;
import com.keyman.engine.data.Keyboard;
import com.keyman.engine.util.CharSequenceUtil;
@ -135,13 +136,13 @@ public class KMKeyboardJSHandler {
end = temp;
}
if (end > start) {
k.setShouldIgnoreSelectionChange(true);
if (s.length() == 0) {
ic.setSelection(start, start);
ic.deleteSurroundingText(0, end - start);
ic.endBatchEdit();
return;
} else {
k.setShouldIgnoreSelectionChange(true);
ic.setSelection(start, start);
ic.deleteSurroundingText(0, end - start);
}
@ -153,7 +154,53 @@ public class KMKeyboardJSHandler {
}
if (s.length() > 0 && s.charAt(0) == '\n') {
keyDownUp(KeyEvent.KEYCODE_ENTER, 0);
if (k.keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
// Special handling of ENTER key
switch (KMManager.enterMode) {
// Go action
case GO :
ic.performEditorAction(EditorInfo.IME_ACTION_GO);
break;
// Search action
case SEARCH :
ic.performEditorAction(EditorInfo.IME_ACTION_SEARCH);
break;
// Send action
case SEND :
ic.performEditorAction(EditorInfo.IME_ACTION_SEND);
break;
// Next action
case NEXT :
ic.performEditorAction(EditorInfo.IME_ACTION_NEXT);
break;
// Done action
case DONE :
ic.performEditorAction(EditorInfo.IME_ACTION_DONE);
break;
// Previous action
case PREVIOUS :
ic.performEditorAction(EditorInfo.IME_ACTION_PREVIOUS);
break;
// Messaging apps
case NEWLINE :
// Send newline and advance cursor
ic.commitText("\n", 1);
break;
// Default ENTER action
default:
keyDownUp(KeyEvent.KEYCODE_ENTER, 0);
}
} else {
// In-app keyboard uses default ENTER action
keyDownUp(KeyEvent.KEYCODE_ENTER, 0);
}
ic.endBatchEdit();
return;
}

View file

@ -25,6 +25,7 @@ import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Typeface;
import android.inputmethodservice.InputMethodService;
import android.net.ConnectivityManager;
@ -32,6 +33,7 @@ import android.net.NetworkInfo;
import android.os.Build;
import android.os.IBinder;
import android.text.InputType;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.Display;
@ -39,6 +41,7 @@ import android.view.Surface;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowMetrics;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
@ -176,6 +179,19 @@ public final class KMManager {
}
}
// Enum for how the System Keyboard ENTER key is handled for the EditorInfo action
// Reference: https://developer.android.com/reference/android/view/inputmethod/EditorInfo#summary
public enum EnterModeType {
GO, // Go action
SEARCH, // Search action
SEND, // Send action
NEXT, // Next action
DONE, // Done action
PREVIOUS, // Previous action
NEWLINE, // Send newline character
DEFAULT, // Default ENTER action
}
protected static InputMethodService IMService;
private static boolean debugMode = false;
@ -214,6 +230,9 @@ public final class KMManager {
// regardless what the Settings preference is.
private static boolean mayPredictOverride = false;
// Determine how system keyboard handles ENTER key
public static EnterModeType enterMode = EnterModeType.DEFAULT;
// Boolean for whether a keyboard can send embedded KMW crash reports to Sentry
// When maySendCrashReport is false, KMW will still attempt to send crash reports, but it
// will be blocked.
@ -244,6 +263,8 @@ public final class KMManager {
public static final String KMKey_KeyboardHeightPortrait = "keyboardHeightPortrait";
public static final String KMKey_KeyboardHeightLandscape = "keyboardHeightLandscape";
public static final String KMKey_LongpressDelay = "longpressDelay";
public static final String KMKey_CustomHelpLink = "CustomHelpLink";
public static final String KMKey_KMPLink = "kmp";
public static final String KMKey_UserKeyboardIndex = "UserKeyboardIndex";
@ -289,11 +310,15 @@ public final class KMManager {
public static final String KMDefault_DictionaryVersion = "0.1.4";
public static final String KMDefault_DictionaryKMP = KMDefault_DictionaryPackageID + FileUtils.MODELPACKAGE;
// Default KeymanWeb longpress delay constants in milliseconds
public static final int KMDefault_LongpressDelay = 500;
public static final int KMMinimum_LongpressDelay = 300;
public static final int KMMaximum_LongpressDelay = 1500;
// Keyman files
protected static final String KMFilename_KeyboardHtml = "keyboard.html";
protected static final String KMFilename_KeyboardHtml_Legacy = "keyboard.es5.html";
protected static final String KMFilename_JSEngine = "keymanweb-webview.js";
protected static final String KMFilename_JSLegacyEngine = "keymanweb-webview.es5.js";
protected static final String KMFilename_JSSentry = "sentry.min.js";
protected static final String KMFilename_JSSentryInit = "keyman-sentry.js";
protected static final String KMFilename_AndroidHost = "android-host.js";
@ -854,25 +879,11 @@ public final class KMManager {
private static void copyAssets(Context context) {
AssetManager assetManager = context.getAssets();
// Will build a temp WebView in order to check Chrome version internally.
boolean legacyMode = WebViewUtils.getEngineWebViewVersionStatus(context, null, null) != WebViewUtils.EngineWebViewVersionStatus.FULL;
try {
// Copy KMW files
if(legacyMode) {
// Replaces the standard ES6-friendly version of the host page with a legacy one that
// includes polyfill requests and that links the legacy, ES5-compatible version of KMW.
copyAssetWithRename(context, KMFilename_KeyboardHtml_Legacy, KMFilename_KeyboardHtml, "", true);
copyAsset(context, KMFilename_KeyboardHtml, "", true);
copyAsset(context, KMFilename_JSLegacyEngine, "", true);
} else {
copyAsset(context, KMFilename_KeyboardHtml, "", true);
// For versions of Chrome with full ES6 support, we use the ES6 artifact.
copyAsset(context, KMFilename_JSEngine, "", true);
}
// Is still built targeting ES5.
copyAsset(context, KMFilename_JSEngine, "", true);
copyAsset(context, KMFilename_JSSentry, "", true);
copyAsset(context, KMFilename_JSSentryInit, "", true);
copyAsset(context, KMFilename_AndroidHost, "", true);
@ -883,12 +894,6 @@ public final class KMManager {
// Copy default keyboard font
copyAsset(context, KMDefault_KeyboardFont, "", true);
if(legacyMode) {
copyAsset(context, KMFilename_JSPolyfill, "", true);
copyAsset(context, KMFilename_JSPolyfill2, "", true);
copyAsset(context, KMFilename_JSPolyfill3, "", true);
}
// Keyboard packages directory
File packagesDir = new File(getPackagesDir());
if (!packagesDir.exists()) {
@ -1269,6 +1274,60 @@ public final class KMManager {
}
}
/**
* Sets enterMode which specifies how the System keyboard ENTER key is handled
*
* @param imeOptions EditorInfo.imeOptions used to determine the action
* @param inputType InputType used to determine if the text field is multi-line
*/
public static void setEnterMode(int imeOptions, int inputType) {
EnterModeType value = EnterModeType.DEFAULT;
int imeActions = imeOptions & EditorInfo.IME_MASK_ACTION;
boolean isMultiLine = (inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE) != 0;
switch (imeActions) {
case EditorInfo.IME_ACTION_GO:
value = EnterModeType.GO;
break;
case EditorInfo.IME_ACTION_SEARCH:
value = EnterModeType.SEARCH;
break;
case EditorInfo.IME_ACTION_SEND:
value = isMultiLine ?
EnterModeType.NEWLINE :EnterModeType.SEND;
break;
case EditorInfo.IME_ACTION_NEXT:
value = EnterModeType.NEXT;
break;
case EditorInfo.IME_ACTION_DONE:
value = isMultiLine ?
EnterModeType.NEWLINE : EnterModeType.DONE;
break;
case EditorInfo.IME_ACTION_PREVIOUS:
value = EnterModeType.PREVIOUS;
break;
default:
value = isMultiLine ?
EnterModeType.NEWLINE : EnterModeType.DEFAULT;
}
enterMode = value;
}
/**
* Get the value of enterMode
* @return EnterModeType
*/
public static EnterModeType getEnterMode() {
return enterMode;
}
/**
* Sets mayPredictOverride true if the InputType field is a hidden password text field
* (either TYPE_TEXT_VARIATION_PASSWORD or TYPE_TEXT_VARIATION_WEB_PASSWORD
@ -1634,7 +1693,7 @@ public final class KMManager {
public static boolean isDefaultKey(String key) {
return (
key != null &&
key != null &&
key.equals(KMString.format("%s_%s", KMDefault_LanguageID, KMDefault_KeyboardID)));
}
@ -2005,7 +2064,20 @@ public final class KMManager {
}
public static int getOrientation(Context context) {
Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
Display display;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// https://developer.android.com/reference/android/content/Context#getDisplay()
try {
display = context.getDisplay();
} catch (UnsupportedOperationException e) {
// if the method is called on an instance that is not associated with any display.
return context.getResources().getConfiguration().orientation;
}
} else {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
// Deprecated in API 30
display = wm.getDefaultDisplay();
}
int rotation = display.getRotation();
if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
return Configuration.ORIENTATION_PORTRAIT;
@ -2015,6 +2087,53 @@ public final class KMManager {
return Configuration.ORIENTATION_UNDEFINED;
}
/**
* Get the longpress delay (in milliseconds) from stored preference. Defaults to 500ms
* @return int - longpress delay in milliseconds
*/
public static int getLongpressDelay() {
SharedPreferences prefs = appContext.getSharedPreferences(
appContext.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE);
return prefs.getInt(KMKey_LongpressDelay, KMDefault_LongpressDelay);
}
/**
* Set the longpress delay (in milliseconds) as a stored preference.
* Valid range is 300 ms to 1500 ms. Returns true if the preference is successfully stored.
* @param longpressDelay - int longpress delay in milliseconds
* @return boolean
*/
public static boolean setLongpressDelay(int longpressDelay) {
if (longpressDelay < KMMinimum_LongpressDelay || longpressDelay > KMMaximum_LongpressDelay) {
return false;
}
SharedPreferences prefs = appContext.getSharedPreferences(
appContext.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(KMKey_LongpressDelay, longpressDelay);
editor.commit();
return true;
}
/**
* Sends options to the KeymanWeb keyboard.
* 1. number of milliseconds to trigger a longpress gesture.
* This method requires a keyboard to be loaded for the value to take effect.
*/
public static void sendOptionsToKeyboard() {
int longpressDelay = getLongpressDelay();
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP)) {
InAppKeyboard.loadJavascript(KMString.format("setLongpressDelay(%d)", longpressDelay));
}
if (SystemKeyboard != null) {
SystemKeyboard.loadJavascript(KMString.format("setLongpressDelay(%d)", longpressDelay));
}
}
public static int getBannerHeight(Context context) {
int bannerHeight = 0;
if (InAppKeyboard != null && InAppKeyboard.getBanner() != BannerType.BLANK) {
@ -2052,6 +2171,34 @@ public final class KMManager {
}
}
/**
* Get the size of the area the window would occupy.
* API 30+
* https://developer.android.com/reference/android/view/WindowManager#getCurrentWindowMetrics()
* @param context
* @return Point (width, height)
*/
public static Point getWindowSize(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
// Deprecated in API 30
Point size = new Point(0, 0);
wm.getDefaultDisplay().getSize(size);
return size;
}
WindowMetrics windowMetrics = wm.getCurrentWindowMetrics();
return new Point(
windowMetrics.getBounds().width(),
windowMetrics.getBounds().height());
}
public static float getWindowDensity(Context context) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
Log.d(TAG, "KMManager: metrics.density " + metrics.density);
return metrics.density;
}
protected static void setPersistentShouldShowHelpBubble(boolean flag) {
SharedPreferences prefs = appContext.getSharedPreferences(appContext.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
@ -2137,17 +2284,40 @@ public final class KMManager {
return result;
}
/**
* Updates the active range for selected text.
* @deprecated
* This method no longer needs the `selStart` and `selEnd` parameters.
* <p>Use {@link KMManager#updateSelectionRange(KeyboardType)} instead.</p>
*
* @param kbType A value indicating if this request is for the in-app keyboard or the system keyboard
* @param selStart (deprecated) the start index for the range
* @param selEnd (deprecated) the end index for the selected range
* @return
*/
@Deprecated
public static boolean updateSelectionRange(KeyboardType kbType, int selStart, int selEnd) {
return updateSelectionRange(kbType);
}
/**
* Performs a synchronization check for the active range for selected text,
* ensuring it matches the text-editor's current state.
* @param kbType A value indicating if this request is for the in-app or system keyboard.
* @return
*/
public static boolean updateSelectionRange(KeyboardType kbType) {
boolean result = false;
if (kbType == KeyboardType.KEYBOARD_TYPE_INAPP) {
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP) && !InAppKeyboard.shouldIgnoreSelectionChange()) {
result = InAppKeyboard.updateSelectionRange(selStart, selEnd);
result = InAppKeyboard.updateSelectionRange();
}
InAppKeyboard.setShouldIgnoreSelectionChange(false);
} else if (kbType == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM) && !SystemKeyboard.shouldIgnoreSelectionChange()) {
result = SystemKeyboard.updateSelectionRange(selStart, selEnd);
result = SystemKeyboard.updateSelectionRange();
}
SystemKeyboard.setShouldIgnoreSelectionChange(false);
@ -2338,7 +2508,7 @@ public final class KMManager {
if (KMManager.shouldAllowSetKeyboard()) {
// inKeyguardRestrictedInputMode() deprecated, so check isKeyguardLocked() to determine if screen is locked
if (isLocked()) {
if (keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM && globeKeyState == GlobeKeyState.GLOBE_KEY_STATE_UP) {
if (keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM && globeKeyState == GlobeKeyState.GLOBE_KEY_STATE_DOWN) {
doGlobeKeyLockscreenAction(context);
}
// clear globeKeyState

View file

@ -10,6 +10,7 @@ import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
@ -31,13 +32,11 @@ final class KMPopoverView extends View {
public KMPopoverView(Context context, AttributeSet attrs) {
super(context, attrs);
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(metrics);
density = metrics.density;
Point size = KMManager.getWindowSize(context);
density = KMManager.getWindowDensity(context);
viewWidth = metrics.widthPixels;
viewHeight = metrics.heightPixels;
viewWidth = size.x;
viewHeight = size.y;
borderRadius = 6 * density;
strokeWidth = 2.0f;
bgColor = context.getResources().getColor(R.color.popup_bg);

View file

@ -63,10 +63,8 @@ public final class KMTextView extends AppCompatEditText {
*/
public static void updateTextContext() {
KMTextView textView = (KMTextView) activeView;
int selStart = textView.getSelectionStart();
int selEnd = textView.getSelectionEnd();
KMManager.updateText(KeyboardType.KEYBOARD_TYPE_INAPP, textView.getText().toString());
if (KMManager.updateSelectionRange(KeyboardType.KEYBOARD_TYPE_INAPP, selStart, selEnd)) {
if (KMManager.updateSelectionRange(KeyboardType.KEYBOARD_TYPE_INAPP)) {
KMManager.resetContext(KeyboardType.KEYBOARD_TYPE_INAPP);
}
}
@ -167,7 +165,7 @@ public final class KMTextView extends AppCompatEditText {
protected void onSelectionChanged(int selStart, int selEnd) {
super.onSelectionChanged(selStart, selEnd);
if (activeView != null && activeView.equals(this)) {
if (KMManager.updateSelectionRange(KMManager.KeyboardType.KEYBOARD_TYPE_INAPP, selStart, selEnd)) {
if (KMManager.updateSelectionRange(KMManager.KeyboardType.KEYBOARD_TYPE_INAPP)) {
KMManager.resetContext(KeyboardType.KEYBOARD_TYPE_INAPP);
}
}

View file

@ -88,7 +88,7 @@ public final class KeyboardInfoActivity extends BaseActivity {
hashMap = new HashMap<String, String>();
final String customHelpLink = kbd.getHelpLink();
// Check if app declared FileProvider
String icon = String.valueOf(R.drawable.ic_arrow_forward);
String icon = String.valueOf(R.drawable.ic_action_forward);
// Don't show help link arrow if it's a local help file and File Provider unavailable,
// or custom help doesn't exist
if ( (customHelpLink != null && ! KMManager.isTestMode() &&

View file

@ -290,6 +290,7 @@ public final class KeyboardPickerActivity extends BaseActivity {
@Override
public void onBackPressed() {
super.onBackPressed();
finish();
}

View file

@ -81,7 +81,7 @@ public final class ModelInfoActivity extends BaseActivity {
final String customHelpLink = lm.getHelpLink();
// Check if app declared FileProvider
// Currently, model help only available if custom link exists
icon = String.valueOf(R.drawable.ic_arrow_forward);
icon = String.valueOf(R.drawable.ic_action_forward);
// Don't show help link arrow if both custom help and File Provider don't exist
// TODO: Update this when model help available on help.keyman.com
if ( (!customHelpLink.equals("") && !FileProviderUtils.exists(context)) ||

View file

@ -273,6 +273,7 @@ public final class ModelPickerActivity extends BaseActivity {
@Override
public void onBackPressed() {
super.onBackPressed();
finish();
}
@ -320,7 +321,7 @@ public final class ModelPickerActivity extends BaseActivity {
// once it has its own backing Dataset instance.
// Is this an installed model or not?
holder.imgDetails.setImageResource(R.drawable.ic_arrow_forward);
holder.imgDetails.setImageResource(R.drawable.ic_action_forward);
if (KeyboardPickerActivity.containsLexicalModel(context, modelKey)) {
holder.imgInstalled.setImageResource(R.drawable.ic_check);
} else {

View file

@ -40,6 +40,7 @@ public class CloudDownloadMgr{
{
if(instance!=null)
return;
KMLog.LogBreadcrumb("CloudDownloadMgr", "CloudDownloadMgr.createInstance() - first call", true);
instance = new CloudDownloadMgr();
}
@ -60,6 +61,7 @@ public class CloudDownloadMgr{
if(isInitialized)
return;
try {
KMLog.LogBreadcrumb("CloudDownloadMgr", "attempting CloudDownloadMgr.initialize()", true);
// Runtime-registered boradcasts receivers must specify export behavior to indicate whether
// or not the receiver should be exported to all other apps on the device
// https://developer.android.com/about/versions/14/behavior-changes-14#runtime-receivers-exported
@ -69,11 +71,12 @@ public class CloudDownloadMgr{
} else {
aContext.registerReceiver(completeListener, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
} catch (IllegalStateException e) {
} catch (IllegalArgumentException e) {
String message = "initialize error: ";
KMLog.LogException(TAG, message, e);
}
isInitialized = true;
KMLog.LogBreadcrumb("CloudDownloadMgr", ".initialize() call complete", false);
}
/**
@ -82,11 +85,15 @@ public class CloudDownloadMgr{
*/
public synchronized void shutdown(Context aContext)
{
if(!isInitialized)
if(!isInitialized) {
return;
}
KMLog.LogBreadcrumb("CloudDownloadMgr", "CloudDownloadMgr.shutdown()", true);
try {
aContext.unregisterReceiver(completeListener);
} catch (IllegalStateException e) {
} catch (IllegalArgumentException e) {
String message = "shutdown error: ";
KMLog.LogException(TAG, message, e);
}
@ -206,8 +213,10 @@ public class CloudDownloadMgr{
CloudApiTypes.CloudApiParam... params)
{
if(!isInitialized) {
Log.w(TAG, "Downloadmanager not initialized. Initializing CloudDownloadMgr.");
Log.w(TAG, "DownloadManager not initialized. Initializing CloudDownloadMgr.");
initialize(aContext);
} else {
KMLog.LogBreadcrumb("CloudDownloadMgr", "CloudDownloadMgr.executeAsDownload() called; already initialized", true);
}
synchronized (downloadSetByDownloadIdentifier) {
@ -218,7 +227,7 @@ public class CloudDownloadMgr{
DownloadManager downloadManager = (DownloadManager) aContext.getSystemService(Context.DOWNLOAD_SERVICE);
if(downloadManager==null)
throw new IllegalStateException("Downloadmanager is not available");
throw new IllegalStateException("DownloadManager is not available");
aCallback.initializeContext(aContext);

View file

@ -13,6 +13,7 @@ import com.keyman.engine.KMManager;
import com.keyman.engine.util.DependencyUtil;
import com.keyman.engine.util.DependencyUtil.LibraryType;
import io.sentry.Breadcrumb;
import io.sentry.Sentry;
import io.sentry.SentryLevel;
@ -34,6 +35,48 @@ public final class KMLog {
}
}
/**
* Utility to log info and add as a Sentry breadcrumb, rather than
* as an independent message
* @param tag String of the caller
* @param msg String of the info message
*/
public static void LogBreadcrumb(String tag, String msg, boolean addStackTrace) {
if (msg == null || msg.isEmpty()) {
return;
}
Log.i(tag, msg);
if (!DependencyUtil.libraryExists(LibraryType.SENTRY) || !Sentry.isEnabled()) {
return;
}
Breadcrumb crumb = new Breadcrumb();
crumb.setMessage(msg);
crumb.setLevel(SentryLevel.INFO);
if(addStackTrace) {
StackTraceElement[] rawTrace = Thread.currentThread().getStackTrace();
// The call that gets us the stack-trace above... shows up in the
// stack trace, so we'll skip the first few (redundant) entries.
int skipCount = 3;
// Sentry does limit the size of messages... so let's just
// keep 10 entries and call it a day.
int limit = Math.min(rawTrace.length, 10 + skipCount);
if(rawTrace.length > skipCount) {
String[] trace = new String[limit - skipCount];
for (int i = skipCount; i < limit; i++) {
trace[i-skipCount] = rawTrace[i].toString();
}
crumb.setData("stacktrace", trace);
}
}
Sentry.addBreadcrumb(crumb);
}
/**
* Utility to log error and send to Sentry
* @param tag String of the caller

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/ic_arrow_back"
android:tint="@android:color/black"
android:autoMirrored="true" />

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