chore(android): Merge remote-tracking branch 'origin/master' into refactor/android/engine-package

This commit is contained in:
Darcy Wong 2023-01-24 09:14:57 +07:00
commit 988bea8e93
89 changed files with 780 additions and 297 deletions

View file

@ -1,5 +1,57 @@
# Keyman Version History
## 17.0.33 alpha 2023-01-23
* chore: Xcode 14.2 update (#8015)
## 17.0.32 alpha 2023-01-20
* fix(ios): use mobile mode for keyboard download pages (#8042)
* docs(common/resources): Update configure step in Docker readme (#8034)
* fix(linux): JSON File missing after installation (#8040)
## 17.0.31 alpha 2023-01-19
* chore: git tag with release@semver (#8035)
* fix(linux): Properly set context after changing IP (#8026)
* chore(linux): use faster zero-length string check (#8037)
* chore(linux): log failures to `km_kbp_context_clear(context)` (#8036)
* chore(linux): Update recommended extension (#8038)
## 17.0.30 alpha 2023-01-17
* chore(linux): Fix vertical alignment of label (#8016)
* fix(common): update sentry release identifiers to support semver (#8031)
## 17.0.29 alpha 2023-01-16
* fix(linux): Fix crash (un-)installing shared keyboard (#8020)
* chore(linux): Don't report KeyboardInterrupt to Sentry (#8021)
* chore(linux): Update sample settings (#8018)
* feat(linux): Enhance tab completion in km-package-install (#8005)
## 17.0.28 alpha 2023-01-12
* feat(linux): Display error messages in the UI (#8006)
* bug(linux): Empty keyboard after failed installation (#8008)
## 17.0.27 alpha 2023-01-11
* chore(linux): Refactor completion script (#8002)
## 17.0.26 alpha 2023-01-10
* feat(linux): Add Back button to "Download Keyman Keyboards" dialog (#7994)
* feat(linux): List fonts in the uninstall confirmation dialog (#7995)
## 17.0.25 alpha 2023-01-09
* chore(linux): Remove unnecessary variable (#7988)
## 17.0.24 alpha 2023-01-06
* chore(common/resources): Add Docker readme (#7980)
## 17.0.23 alpha 2023-01-03
* fix(linux): add IBUS_HAS_PREFILTER ifdef to linux/ibus-keyman/tests (#7958)
@ -106,6 +158,27 @@
* chore: move to 17.0-alpha (#7577)
* chore: Move to 17.0 alpha
## 16.0.133 beta 2023-01-19
* fix(linux): Properly set context after changing IP (#8025)
## 16.0.132 beta 2023-01-18
* docs(windows): update screenshots and documentation for Keyman for Windows config (#8014)
## 16.0.131 beta 2023-01-16
* chore(linux): Don't report KeyboardInterrupt to Sentry (#8022)
* fix(linux): Fix crash (un-)installing shared keyboard (#8019)
## 16.0.130 beta 2023-01-09
* fix(windows): kmshell -ikl install language and enable keyboard (#7856)
## 16.0.129 beta 2023-01-06
* fix(android/engine): Add utility for localized strings (#7976)
## 16.0.128 beta 2022-12-22
* fix(developer): force ES3 code generation for LMs (#7927)

View file

@ -1 +1 @@
17.0.24
17.0.34

View file

@ -36,7 +36,7 @@ display_usage ( ) {
}
function makeLocalSentryRelease() {
local SENTRY_RELEASE_VERSION="release-$VERSION_WITH_TAG"
local SENTRY_RELEASE_VERSION="release@$VERSION_WITH_TAG"
echo "Making a Sentry release for tag $SENTRY_RELEASE_VERSION"
sentry-cli upload-dif -p keyman-android --include-sources
sentry-cli releases -p keyman-android files $SENTRY_RELEASE_VERSION upload-sourcemaps ./

View file

@ -55,7 +55,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.setRelease("release-"+com.tavultesoft.kmapro.BuildConfig.VERSION_NAME);
options.setRelease("release@"+com.tavultesoft.kmapro.BuildConfig.VERSION_NAME);
options.setEnvironment(com.tavultesoft.kmapro.BuildConfig.VERSION_ENVIRONMENT);
});
}

View file

@ -131,7 +131,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
checkSendCrashReport();
if (KMManager.getMaySendCrashReport()) {
SentryAndroid.init(context, options -> {
options.setRelease("release-" + com.tavultesoft.kmapro.BuildConfig.VERSION_NAME);
options.setRelease("release@" + com.tavultesoft.kmapro.BuildConfig.VERSION_NAME);
options.setEnvironment(com.tavultesoft.kmapro.BuildConfig.VERSION_ENVIRONMENT);
});
}

View file

@ -16,10 +16,12 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.preference.PreferenceManager;
import com.keyman.engine.util.ContextUtils;
import com.keyman.engine.util.KMLog;
import java.util.Locale;
public class BaseActivity extends AppCompatActivity {
private static final String TAG = "BaseActivity";
static ContextWrapper localeUpdatedContext;
/**
@ -49,6 +51,23 @@ public class BaseActivity extends AppCompatActivity {
}
}
/**
* Some classes aren't an AppCompatActivity and need this helper to retrieve localized Strings
* in the updated locale.
* @param defaultContext - the context to fallback if localUpdatedContext is null
* @param resID - the resource ID of the string
* @return String - localized string
*/
public static String getString(Context defaultContext, int resID) {
Context context = (localeUpdatedContext != null) ? localeUpdatedContext : defaultContext;
if (context != null) {
return context.getString(resID);
};
// Shouldn't be here
KMLog.LogError(TAG, "context null for getString()");
return "";
}
@Override
protected void attachBaseContext(Context newBase) {
// Override the app locale using the BCP 47 tag from shared preferences

View file

@ -1288,7 +1288,7 @@ final class KMKeyboard extends WebView {
}
try {
String hintText = context.getString(R.string.help_bubble_text);
String hintText = BaseActivity.getString(this.context, R.string.help_bubble_text);
// To ensure that the localized text is properly escaped, we'll use JSON utilities. Since
// there's no direct string encoder, we'll just wrap it in an object and unwrap it in JS.

View file

@ -59,7 +59,7 @@ if builder_start_action build; then
static readonly VERSION_TAG = \"$VERSION_TAG\";
static readonly VERSION_WITH_TAG = \"$VERSION_WITH_TAG\";
static readonly VERSION_ENVIRONMENT = \"$VERSION_ENVIRONMENT\";
static readonly SENTRY_RELEASE = \"release-$VERSION_WITH_TAG\";
static readonly SENTRY_RELEASE = \"release@$VERSION_WITH_TAG\";
}
}
" > ./version.inc.ts

View file

@ -49,7 +49,7 @@ int keyman_sentry_init(bool is_keyman_developer, const char *logger) {
ILFree(pidl);
}
sentry_options_set_release(options, "release-" KEYMAN_VersionWithTag); // matches git tag
sentry_options_set_release(options, "release@" KEYMAN_VersionWithTag); // matches git tag
sentry_options_set_environment(options, KEYMAN_Environment); // stable, beta, alpha, test, local
// We don't currently need to set this, because it will be same path

View file

@ -341,7 +341,7 @@ begin
// Note: system proxy is used automatically if no proxy is defined
o.Release := 'release-'+CKeymanVersionInfo.VersionWithTag; // matches git tag
o.Release := 'release@'+CKeymanVersionInfo.VersionWithTag; // matches git tag
o.Environment := CKeymanVersionInfo.Environment; // stable, beta, alpha, test, local
if kscfCaptureExceptions in FFlags

View file

@ -14,6 +14,11 @@ using namespace kmx;
#include <chrono>
/**
* \def MEDIUM_BUFFER_SIZE not too big, not too small
*/
#define MEDIUM_BUFFER_SIZE (128 * 7)
#ifdef _MSC_VER
#define _USE_WINDOWS
#endif
@ -58,7 +63,7 @@ int km::kbp::kmx::DebugLog_1(const char *file, int line, const char *function, c
return 0;
char windowinfo[1024];
sprintf(windowinfo,
snprintf(windowinfo, 1024,
"%ld" TAB //"TickCount" TAB
"%s:%d" TAB //"SourceFile" TAB
"%s" TAB //"Function"
@ -108,10 +113,10 @@ const char *km::kbp::kmx::Debug_VirtualKey(KMX_WORD vk) {
}
if (vk < 256) {
sprintf(buf, "['%s' 0x%x]", s_key_names[vk], vk);
snprintf(buf, 256, "['%s' 0x%x]", s_key_names[vk], vk);
}
else {
sprintf(buf, "[0x%x]", vk);
snprintf(buf, 256, "[0x%x]", vk);
}
return buf;
}
@ -129,7 +134,7 @@ const char *km::kbp::kmx::Debug_UnicodeString(PKMX_WCHAR s, int x) {
bufout[x][0] = 0;
for (p = s, q = bufout[x]; *p && (p - s < 128); p++)
{
sprintf(q, "U+%4.4X ", *p);
snprintf(q, MEDIUM_BUFFER_SIZE, "U+%4.4X ", *p);
q = strchr(q, 0);
}
//WideCharToMultiByte(CP_ACP, 0, buf, -1, bufout, 128, NULL, NULL);
@ -149,7 +154,7 @@ const char *km::kbp::kmx::Debug_UnicodeString(std::u16string s, int x) {
bufout[x][0] = 0;
for (q = bufout[x]; (intptr_t)(q-bufout[x]) < (128*7) && p != s.end(); p++)
{
sprintf(q, "U+%4.4X ", *p); q = strchr(q, 0);
snprintf(q, MEDIUM_BUFFER_SIZE, "U+%4.4X ", *p); q = strchr(q, 0);
}
return bufout[x];
}

View file

@ -4,7 +4,7 @@ const Sentry = require("@sentry/node");
Sentry.init({
dsn: 'https://39b25a09410349a58fe12aaf721565af@o1005580.ingest.sentry.io/5983519', // Keyman Developer
environment: environment.versionEnvironment,
release: environment.versionWithTag
release: 'release@'+environment.versionWithTag
});
import express = require('express');

View file

@ -10,7 +10,7 @@
Sentry.init({
dsn: 'https://39b25a09410349a58fe12aaf721565af@o1005580.ingest.sentry.io/5983519', // Keyman Developer
environment: '$VERSION_ENVIRONMENT',
release: 'release-$VERSION_WITH_TAG'
release: 'release@$VERSION_WITH_TAG'
});
function keymanEnableDiagnostics() {

View file

@ -10,7 +10,7 @@
Sentry.init({
dsn: 'https://39b25a09410349a58fe12aaf721565af@o1005580.ingest.sentry.io/5983519', // Keyman Developer
environment: '$Environment',
release: 'release-$VersionWithTag'
release: 'release@$VersionWithTag'
});
function keymanEnableDiagnostics() {

View file

@ -41,4 +41,4 @@ cd "$KEYMAN_ROOT/developer/src"
echo "Uploading symbols for developer/"
sentry-cli upload-dif -p keyman-developer -t breakpad -t pdb . --include-sources
sentry-cli releases -p keyman-developer files "release-$VERSION_WITH_TAG" upload-sourcemaps ./TIKE/xml ../bin/server ./kmlmc/dist
sentry-cli releases -p keyman-developer files "release@$VERSION_WITH_TAG" upload-sourcemaps ./TIKE/xml ../bin/server ./kmlmc/dist

View file

@ -2,11 +2,12 @@
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"asabil.meson",
"mesonbuild.mesonbuild",
"editorconfig.editorconfig",
"ms-python.python",
"mads-hartmann.bash-ide-vscode",
"timonwong.shellcheck",
"maelvalais.autoconf"
"maelvalais.autoconf",
"webfreak.debug",
]
}

View file

@ -24,14 +24,17 @@
"valuesFormatting": "parseText"
},
{
// Mount the Keyman root directory as a Shared Folder in the VM.
// kill ibus-engine-keyman, if running
// start: gdbserver 10.3.0.53:2345 /media/sf_Develop/keyman/keyman/linux/ibus-keyman/src/ibus-engine-keyman
// then attach debugger in vscode
// Replace the IP address after `gdbserver` with the IP address of the host machine.
// Replace the IP address below with the address of the VM running gdbserver.
"type": "gdb",
"request": "attach",
"name": "Attach to gdbserver",
"executable": "./linux/ibus-keyman/src/ibus-engine-keyman",
"target": "10.3.0.53:2345",
"name": "Attach to gdbserver (ibus-keyman)",
"executable": "${workspaceFolder}/linux/ibus-keyman/src/ibus-engine-keyman",
"target": "10.3.0.52:2345",
"remote": true,
"cwd": "${workspaceFolder}/linux/ibus-keyman",
"valuesFormatting": "parseText"

View file

@ -143,9 +143,9 @@
"CPPFLAGS=\"-DG_MESSAGES_DEBUG -I${workspaceFolder}/core/build/arch/debug/include/ -I${workspaceFolder}/common/include/ -I${workspaceFolder}/core/include/\"",
"CFLAGS=\"-g -O0\"",
"CXXFLAGS=\"-g -O0\"",
"KEYMAN_PROC_LIBS=\"-L${workspaceFolder}/common/core/desktop/build/arch/debug/src -lkmnkbp0\"",
"KEYMAN_PROC_CFLAGS=\"-I${workspaceFolder}/common/core/desktop/build/arch/debug/include -I${workspaceFolder}/common/core/desktop/include\"",
"PKG_CONFIG_PATH=${workspaceFolder}/common/core/desktop/build/arch/debug/meson-private"
"KEYMAN_PROC_LIBS=\"-L${workspaceFolder}/core/build/arch/debug/src -lkmnkbp0\"",
"KEYMAN_PROC_CFLAGS=\"-I${workspaceFolder}/core/build/arch/debug/include -I${workspaceFolder}/core/include -I${workspaceFolder}/common/include\"",
"PKG_CONFIG_PATH=${workspaceFolder}/core/build/arch/debug/meson-private"
],
"options": {
"cwd": "${workspaceFolder}/linux/ibus-keyman"

117
docs/websites/README.md Normal file
View file

@ -0,0 +1,117 @@
# How to Set Up a Local Web Server for the Keyman Web Pages
Currently, most of the Keyman websites are running via IIS. Refer to the Keyman [wiki](https://github.com/keymanapp/keyman/wiki/How-to-set-up-a-local-web-server-for-the-Keyman-web-pages) for setting that up on Windows 10.
To make IIS redirects compatible with the Docker sites below, see
https://github.com/keymanapp/keyman.com/issues/337#issuecomment-1336339387
As the websites get migrated to Apache via Docker, follow the installation steps below:
## Pre-requisite Installs
* [Docker Desktop](https://www.docker.com/products/docker-desktop/)
On Windows, Docker will need either:
* Hyper-V or
* WSL 2.0 - Install with this guide:
https://learn.microsoft.com/en-us/windows/wsl/tutorials/wsl-containers
* WSL will then need a Linux image (e.g. Ubuntu app) from the Microsoft Store
#### Other Docker Notes
Docker tends to throttle Docker image downloads, so some developer offices may want to set up a proxy server. If the proxy server is set up, carefully edit the JSON file per in Docker Settings -> Docker Engine https://docs.docker.com/registry/recipes/mirror/#configure-the-docker-daemon and click 'Apply & Restart'. Note the example (lingnet) is for running inside the Linguistics Institute (Chiang Mai)
```
"registry-mirrors": ["https://docker.io.registry.lingnet/"],
"insecure-registries" : [
"docker.io.registry.lingnet",
"registry.lingnet"
]
```
## Builder BASH Script Actions
#### Stop the Docker container
1. Run `./build.sh stop`
This stops the Docker container for the site.
#### Build the Docker image
1. Run `./build.sh build`.
This downloads and builds the Docker images needed for the site.
#### Configure
1. Run `./build.sh configure`.
This step is currently not needed
#### Start the Docker container
1. Run `./build.sh start`.
This maps the local directory to the the Docker image.
Then, it creates a link of the PHP dependencies in Docker image from /var/www/vendor/ to /var/www/html/vendor.
The link file also appears locally.
After this, you can access the website at the following ports:
| Website | URL |
|--------------|-----------------------|
|help.keyman | http://localhost:8055 |
|keymanweb.com | http://localhost:8057 |
#### Remove the Docker container and image
1. Run `./build.sh clean`.
#### Running tests
Checks for broken links
1. Run `./build.sh test`
---------
## Kubernetes Deployment
For production, the websites are deployed with Kubernetes.
### How to run help.keyman.com locally with Docker Desktop's Kubernetes singlenode cluster
For testing Kubernetes deployment, there are yaml files under the corresponding website's repo: `/resources/kubectl`, that cover local developer testing.
### Pre-requisites
On the host machine, install [Docker](https://docs.docker.com/get-docker/), then enable Kubernetes in the settings. Ensure you have built a help-keyman-app Docker image, and either tag it `docker.dallas.languagetechnology.org/keyman/help-keyman-app` or modify the `app-php` containers `image:` value to match you local copy's name.
### Deploying to a desktop cluster
To deploy the dev version to the cluster do the following:
1. Ensure your `kubectl` context is set to `docker-desktop`, though the Docker Desktop systray icon or by running:
```bash
$> kubectl config use-context docker-desktop
```
2. Create a keyman namespace if it does not already exist:
```bash
$> kubectl create ns keyman
```
3. Apply the configs for the resources and start the pod:
```bash
$> kubectl --namespace keyman apply \
-f resources/kubectl/help-kubectl-dev.yaml \
-f resources/kubectl/help-kubectl.yaml
```
### Testing the site and `/api/deploy` webhook endpoint
The site can be reached on http://localhost:30080/ via web browser, and the deploy api is on http://localhost:30900/api/deploy, and can be activated like so:
```bash
$> curl -v --request POST \
-H "Content-Type: application/json" \
-H "X-Hub-Signature-256: sha256=49af8531106a369bfee369f91dadec597e8ea3992ec2802bbe655be0ece17f15" \
--data '{"action":"push","ref":"refs/heads/staging"}' \
http://localhost:30900/api/deploy
```
This simulates enough of a GitHub webhook push event to pass validation on the responder.
### Clean up after testing
To remove the k8s pod and resources, and delete everything do:
```bash
$> kubectl --namespace=keyman delete {pod,cm,svc,secret,pvc}/help-keyman-com
```
Or just delete the pod and keep the resources for further testing:
```bash
$> kubectl --namespace=keyman delete pod/help-keyman-com
```

View file

@ -1,5 +1,5 @@
github "marmelroy/Zip"
github "DaveWoodCom/XCGLogger" ~> 6.1.0
github "devicekit/DeviceKit" ~> 2.3
github "devicekit/DeviceKit" ~> 5.0
github "ashleymills/Reachability.swift"
github "getsentry/sentry-cocoa" ~> 6.2.1
github "getsentry/sentry-cocoa" ~> 6.2.1

View file

@ -1,5 +1,5 @@
github "DaveWoodCom/XCGLogger" "6.1.0"
github "ashleymills/Reachability.swift" "v5.0.0"
github "devicekit/DeviceKit" "2.3.0"
github "ashleymills/Reachability.swift" "v5.1.0"
github "devicekit/DeviceKit" "5.0.0"
github "getsentry/sentry-cocoa" "6.2.1"
github "marmelroy/Zip" "1.1.0"
github "marmelroy/Zip" "2.1.2"

View file

@ -1670,7 +1670,7 @@
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
INFOPLIST_FILE = SystemKeyboard/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -1716,7 +1716,7 @@
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
INFOPLIST_FILE = SystemKeyboard/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -1851,7 +1851,7 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_BITCODE = YES;
ENABLE_BITCODE = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
@ -1859,7 +1859,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
INFOPLIST_FILE = KeymanEngine/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -1907,14 +1907,14 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_BITCODE = YES;
ENABLE_BITCODE = NO;
ENABLE_NS_ASSERTIONS = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
INFOPLIST_FILE = KeymanEngine/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -1938,7 +1938,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = F27FCAF6157FD95E00FBBA20 /* Keyman-lib.xcconfig */;
buildSettings = {
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
PRODUCT_NAME = "$(TARGET_NAME)";
STRIP_INSTALLED_PRODUCT = NO;
STRIP_STYLE = all;
@ -1951,7 +1951,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = F27FCAF6157FD95E00FBBA20 /* Keyman-lib.xcconfig */;
buildSettings = {
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTS_MACCATALYST = NO;
};
@ -1980,7 +1980,7 @@
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
INFOPLIST_FILE = "KeymanEngine/resources/Keyman.bundle/Contents/Resources/KeymanEngine-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -2017,7 +2017,7 @@
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
INFOPLIST_FILE = "KeymanEngine/resources/Keyman.bundle/Contents/Resources/KeymanEngine-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -2069,7 +2069,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
KEYMAN_ROOT = "$(SRCROOT)/../../..";
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
@ -2102,7 +2102,7 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_BITCODE = YES;
ENABLE_BITCODE = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
@ -2110,7 +2110,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
INFOPLIST_FILE = KeymanEngine/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -2134,7 +2134,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = F27FCAF6157FD95E00FBBA20 /* Keyman-lib.xcconfig */;
buildSettings = {
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTS_MACCATALYST = NO;
};
@ -2153,7 +2153,7 @@
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 3YE4W86L3G;
ENABLE_BITCODE = NO;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -2203,7 +2203,7 @@
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
INFOPLIST_FILE = SystemKeyboard/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -2292,7 +2292,7 @@
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
INFOPLIST_FILE = "KeymanEngine/resources/Keyman.bundle/Contents/Resources/KeymanEngine-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -2344,7 +2344,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
KEYMAN_ROOT = "$(SRCROOT)/../../..";
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG NO_SENTRY";
@ -2387,7 +2387,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
KEYMAN_ROOT = "$(SRCROOT)/../../..";
SKIP_INSTALL = YES;
SWIFT_COMPILATION_MODE = wholemodule;
@ -2408,7 +2408,7 @@
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 3YE4W86L3G;
ENABLE_BITCODE = NO;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -2441,7 +2441,7 @@
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 3YE4W86L3G;
ENABLE_BITCODE = NO;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",

View file

@ -35,7 +35,7 @@ public class SentryManager {
let infoDict = Bundle(for: SentryManager.self).infoDictionary
let versionWithTag = infoDict?["KeymanVersionWithTag"] as? String ?? ""
let environment = infoDict?["KeymanVersionEnvironment"] as? String ?? ""
let release = "release-\(versionWithTag)"
let release = "release@\(versionWithTag)"
let options = Sentry.Options()
options.dsn = "https://d14d2efb594e4345b8367dbb61ebceaf@o1005580.ingest.sentry.io/5983521"

View file

@ -43,6 +43,16 @@ class PackageWebViewController: UIViewController, WKNavigationDelegate {
let prefs = WKPreferences()
prefs.javaScriptEnabled = true
if #available(iOS 13.0, *) {
/**
In iPadOS 16 and above WKWebView defaults to lying about its user-agent,
telling the web server that it is a mac. We can avoid this with .mobile:
*/
let pref = WKWebpagePreferences.init()
pref.preferredContentMode = .mobile
config.defaultWebpagePreferences = pref
}
// Inject a meta viewport tag into the head of the file if it doesn't exist
let metaViewportInjection = """
if(!document.querySelectorAll('meta[name=viewport]').length) {

View file

@ -101,7 +101,20 @@ public class KeyboardSearchViewController: UIViewController, WKNavigationDelegat
}
public override func loadView() {
let webView = WKWebView()
let config = WKWebViewConfiguration()
if #available(iOS 13.0, *) {
/**
In iPadOS 16 and above WKWebView defaults to lying about its user-agent,
telling the web server that it is a mac. We can avoid this with .mobile:
*/
let pref = WKWebpagePreferences.init()
pref.preferredContentMode = .mobile
config.defaultWebpagePreferences = pref
}
let webView = WKWebView.init(frame: CGRect.zero, configuration: config)
webView.navigationDelegate = self
if let languageCode = languageCode {
let baseURL = KeyboardSearchViewController.ENDPOINT_ROOT

View file

@ -1123,7 +1123,7 @@
GCC_PREFIX_HEADER = "Keyman/Keyman-Prefix.pch";
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
INFOPLIST_FILE = Keyman/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -1158,7 +1158,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Keyman/Keyman-Prefix.pch";
INFOPLIST_FILE = Keyman/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -1225,7 +1225,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
KEYMAN_ROOT = "$(SRCROOT)/../../..";
ONLY_ACTIVE_ARCH = NO;
OTHER_LDFLAGS = "";
@ -1277,7 +1277,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
KEYMAN_ROOT = "$(SRCROOT)/../../..";
ONLY_ACTIVE_ARCH = NO;
OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
@ -1312,7 +1312,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = SWKeyboard/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -1355,7 +1355,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = SWKeyboard/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -1424,7 +1424,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
KEYMAN_ROOT = "$(SRCROOT)/../../..";
ONLY_ACTIVE_ARCH = NO;
OTHER_LDFLAGS = "";
@ -1449,7 +1449,7 @@
GCC_PREFIX_HEADER = "Keyman/Keyman-Prefix.pch";
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
INFOPLIST_FILE = Keyman/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -1493,7 +1493,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = SWKeyboard/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",

View file

@ -222,12 +222,6 @@ if [ $DO_CARTHAGE = true ]; then
carthage checkout || fail "Carthage dependency loading failed"
# Carthage sometimes picks the wrong .xcworkspace if two are available in a dependency's repo.
# Easiest way to override it - delete the wrong one (or just its scheme)
# Deleted workspace - a test for proper deployment to CocoaPods. Doesn't matter here.
rm -r ./Carthage/Checkouts/DeviceKit/CocoaPodsVerification/ || fail "Carthage dependency loading failed"
# --no-use-binaries: due to https://github.com/Carthage/Carthage/issues/3134,
# which affects the sentry-cocoa dependency.
carthage build --use-xcframeworks --no-use-binaries --platform iOS || fail "Carthage dependency loading failed"

View file

@ -461,6 +461,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_BITCODE = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
FRAMEWORK_SEARCH_PATHS = (
@ -518,6 +519,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_BITCODE = NO;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_SEARCH_PATHS = (
@ -552,6 +554,7 @@
CODE_SIGN_ENTITLEMENTS = KMSample2/KMSample2.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = KMSample2/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
LD_RUNPATH_SEARCH_PATHS = (
@ -577,6 +580,7 @@
CODE_SIGN_ENTITLEMENTS = KMSample2/KMSample2.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = KMSample2/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
LD_RUNPATH_SEARCH_PATHS = (
@ -599,6 +603,7 @@
CODE_SIGN_ENTITLEMENTS = SWKeyboard/SWKeyboard.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
@ -628,6 +633,7 @@
CODE_SIGN_ENTITLEMENTS = SWKeyboard/SWKeyboard.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = SWKeyboard/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
LD_RUNPATH_SEARCH_PATHS = (

View file

@ -390,7 +390,7 @@
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = WHE552KZN5;
INFOPLIST_FILE = CalibrationKbd/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -411,7 +411,7 @@
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = WHE552KZN5;
INFOPLIST_FILE = CalibrationKbd/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -548,7 +548,7 @@
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = WHE552KZN5;
INFOPLIST_FILE = KeyboardCalibrator/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -569,7 +569,7 @@
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = WHE552KZN5;
INFOPLIST_FILE = KeyboardCalibrator/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",

View file

@ -37,11 +37,7 @@ private class CustomInputView: UIInputView {
func setConstraints() {
var guide: UILayoutGuide
if #available(iOS 11.0, *) {
guide = self.safeAreaLayoutGuide
} else {
guide = self.layoutMarginsGuide
}
guide = self.safeAreaLayoutGuide
if height != 0 {
innerView.heightAnchor.constraint(equalToConstant: height).isActive = true
@ -49,15 +45,9 @@ private class CustomInputView: UIInputView {
innerView.heightAnchor.constraint(equalTo: guide.heightAnchor).isActive = true
}
if #available(iOS 11.0, *) {
innerView.widthAnchor.constraint(equalTo: guide.widthAnchor).isActive = true
innerView.leftAnchor.constraint(equalTo: guide.leftAnchor).isActive = true
insetView.widthAnchor.constraint(equalTo: guide.widthAnchor).isActive = true
} else {
innerView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
innerView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
insetView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
}
innerView.widthAnchor.constraint(equalTo: guide.widthAnchor).isActive = true
innerView.leftAnchor.constraint(equalTo: guide.leftAnchor).isActive = true
insetView.widthAnchor.constraint(equalTo: guide.widthAnchor).isActive = true
innerView.bottomAnchor.constraint(equalTo: insetView.topAnchor).isActive = true
@ -172,13 +162,8 @@ class DummyInputViewController: UIInputViewController {
self.view.addSubview(self.nextKeyboardButton)
var guide: UILayoutGuide
if #available(iOS 11.0, *) {
guide = self.view.safeAreaLayoutGuide
self.nextKeyboardButton.isHidden = !self.needsInputModeSwitchKey || !asSystemKeyboard
} else {
guide = self.view.layoutMarginsGuide
self.nextKeyboardButton.isHidden = false // Seems buggy to vary, and it matters greatly on older devices.
}
guide = self.view.safeAreaLayoutGuide
self.nextKeyboardButton.isHidden = !self.needsInputModeSwitchKey || !asSystemKeyboard
self.nextKeyboardButton.leftAnchor.constraint(equalTo: guide.leftAnchor).isActive = true
self.nextKeyboardButton.bottomAnchor.constraint(equalTo: guide.bottomAnchor).isActive = true

View file

@ -149,11 +149,7 @@ class ViewController: UIViewController {
@IBAction func performCapture() {
let rect = kbdFrame?.cgRectValue
if #available(iOS 11.0, *) {
capture.insetBottom = self.view.safeAreaInsets.bottom
} else {
capture.insetBottom = self.view.layoutMargins.bottom
}
capture.insetBottom = self.view.safeAreaInsets.bottom
// Determine correct capture mode
switch captureMode {

View file

@ -86,6 +86,7 @@ Depends:
python3-bs4,
python3-gi,
python3-sentry-sdk (>= 1.1) | python3-raven,
dbus-x11,
${misc:Depends},
${python3:Depends},
Description: Keyman for Linux configuration

View file

@ -123,11 +123,11 @@ static void ibus_keyman_engine_focus_out (IBusEngine *engine);
static void ibus_keyman_engine_reset (IBusEngine *engine);
static void ibus_keyman_engine_enable (IBusEngine *engine);
static void ibus_keyman_engine_disable (IBusEngine *engine);
// static void ibus_keyman_engine_set_surrounding_text
// (IBusEngine *engine,
// IBusText *text,
// guint cursor_pos,
// guint anchor_pos);
static void ibus_keyman_engine_set_surrounding_text
(IBusEngine *engine,
IBusText *text,
guint cursor_pos,
guint anchor_pos);
// static void ibus_keyman_engine_set_cursor_location
// (IBusEngine *engine,
// guint x,
@ -201,7 +201,7 @@ ibus_keyman_engine_class_init (IBusKeymanEngineClass *klass)
engine_class->enable = ibus_keyman_engine_enable;
engine_class->disable = ibus_keyman_engine_disable;
// engine_class->set_surrounding_text = ibus_keyman_engine_set_surrounding_text;
engine_class->set_surrounding_text = ibus_keyman_engine_set_surrounding_text;
// engine_class->set_cursor_location = ibus_keyman_engine_set_cursor_location;
@ -272,17 +272,22 @@ reset_context(IBusEngine *engine) {
surrounding_text, context_end - context_start, cursor_pos, anchor_pos);
current_context_utf8 = get_current_context_text(context);
if (!g_str_has_suffix(surrounding_text, current_context_utf8) || !g_utf8_strlen(current_context_utf8, -1)) {
if (!(*current_context_utf8) || !g_str_has_suffix(surrounding_text, current_context_utf8)) {
g_message("%s: setting context because it has changed from expected", __FUNCTION__);
if (km_kbp_context_items_from_utf8(surrounding_text, &context_items) == KM_KBP_STATUS_OK) {
enum km_kbp_status_codes status = km_kbp_context_items_from_utf8(surrounding_text, &context_items);
if (status == KM_KBP_STATUS_OK) {
km_kbp_context_set(context, context_items);
km_kbp_context_items_dispose(context_items);
} else {
km_kbp_context_clear(context);
g_message("%s: setting context failed with status code %d", __FUNCTION__, status);
}
}
g_free(surrounding_text);
g_free(current_context_utf8);
} else {
km_kbp_context_clear(context);
g_message("%s: client does not support surrounding text", __FUNCTION__);
}
}
@ -978,23 +983,23 @@ ibus_keyman_engine_process_key_event(
return TRUE;
}
// static void
// ibus_keyman_engine_set_surrounding_text (IBusEngine *engine,
// IBusText *text,
// guint cursor_pos,
// guint anchor_pos)
// {
// gchar *surrounding_text;
// guint context_start = cursor_pos > MAXCONTEXT_ITEMS ? cursor_pos - MAXCONTEXT_ITEMS : 0;
// if (cursor_pos != anchor_pos){
// g_message("%s: There is a selection", __FUNCTION__);
// }
// parent_class->set_surrounding_text (engine, text, cursor_pos, anchor_pos);
// surrounding_text = g_utf8_substring(ibus_text_get_text(text), context_start, cursor_pos);
// g_message("%s: surrounding context is:%u:%s:", __FUNCTION__, cursor_pos - context_start, surrounding_text);
// g_free(surrounding_text);
// reset_context(engine);
// }
static void
ibus_keyman_engine_set_surrounding_text (IBusEngine *engine,
IBusText *text,
guint cursor_pos,
guint anchor_pos)
{
// gchar *surrounding_text;
// guint context_start = cursor_pos > MAXCONTEXT_ITEMS ? cursor_pos - MAXCONTEXT_ITEMS : 0;
// if (cursor_pos != anchor_pos){
// g_message("%s: There is a selection", __FUNCTION__);
// }
parent_class->set_surrounding_text (engine, text, cursor_pos, anchor_pos);
// surrounding_text = g_utf8_substring(ibus_text_get_text(text), context_start, cursor_pos);
// g_message("%s: surrounding context is:%u:%s:", __FUNCTION__, cursor_pos - context_start, surrounding_text);
// g_free(surrounding_text);
reset_context(engine);
}
// static void ibus_keyman_engine_set_cursor_location (IBusEngine *engine,
// guint x,

View file

@ -37,6 +37,15 @@ def secure_lookup(data, key1, key2 = None):
return None
def before_send(event, hint):
if 'exc_info' in hint:
exc_type, exc_value, tb = hint['exc_info']
if isinstance(exc_value, KeyboardInterrupt):
# Ignore KeyboardInterrupt exception
return None
return event
gettext.bindtextdomain('keyman-config', '/usr/share/locale')
gettext.textdomain('keyman-config')
@ -75,8 +84,9 @@ else:
sentry_sdk.init(
dsn=SentryUrl,
environment=__environment__,
release='release-' + __versionwithtag__,
release='release@' + __versionwithtag__,
integrations=[sentry_logging],
before_send=before_send
)
set_user({'id': hash(getpass.getuser())})
with configure_scope() as scope:
@ -94,7 +104,7 @@ else:
# Note, legacy raven API requires secret (https://github.com/keymanapp/keyman/pull/5787#discussion_r721457909)
SentryUrl = "https://1d0edbf2d0dc411b87119b6e92e2c357:e6d5a81ee6944fc79bd9f0cbb1f2c2a4@o1005580.ingest.sentry.io/5983525"
client = Client(SentryUrl, environment=__environment__, release='release-' + __versionwithtag__)
client = Client(SentryUrl, environment=__environment__, release='release@' + __versionwithtag__)
client.user_context({'id': hash(getpass.getuser())})
client.tags_context({
'app': os.path.basename(sys.argv[0]),

View file

@ -30,13 +30,26 @@ class DownloadKmpWindow(Gtk.Dialog):
s = Gtk.ScrolledWindow()
self.webview = WebKit2.WebView()
self.webview.connect("decide-policy", self._keyman_policy)
self.webview.connect("load-changed", self._update_back_button)
url = KeymanComUrl + "/go/linux/" + __releaseversion__ + "/download-keyboards"
self.webview.load_uri(url)
s.add(self.webview)
self.get_content_area().pack_start(s, True, True, 0)
self.add_button(_("_Close"), Gtk.ResponseType.CLOSE)
hbox = Gtk.Box(spacing=6)
self.get_content_area().pack_start(hbox, False, False, 0)
self.back_button = Gtk.Button.new_with_mnemonic(_("_Back"))
self.back_button.set_tooltip_text(_("Back to search"))
self.back_button.connect("clicked", self._on_back_clicked)
self.back_button.set_sensitive(False)
hbox.pack_start(self.back_button, False, False, 0)
close_button = Gtk.Button.new_with_mnemonic(_("_Close"))
close_button.set_tooltip_text(_("Close dialog"))
close_button.connect("clicked", self._on_close_clicked)
hbox.pack_end(close_button, False, False, 0)
if self.parentWindow is not None:
self.getinfo = GetInfo(self.parentWindow.incomplete_kmp)
@ -44,6 +57,15 @@ class DownloadKmpWindow(Gtk.Dialog):
self.resize(800, 450)
self.show_all()
def _update_back_button(self, webview, load_event):
self.back_button.set_sensitive(webview.can_go_back())
def _on_back_clicked(self, button):
self.webview.go_back()
def _on_close_clicked(self, button):
self.response(Gtk.ResponseType.CLOSE)
def _process_kmp(self, url, downloadfile: str):
logging.info("Downloading kmp file to %s", downloadfile)
if download_kmp_file(url, downloadfile):
@ -52,6 +74,12 @@ class DownloadKmpWindow(Gtk.Dialog):
self.response(Gtk.ResponseType.OK)
self.close()
return True
logging.error(_("Downloading kmp file failed"))
dialog = Gtk.MessageDialog(
self, 0, Gtk.MessageType.ERROR,
Gtk.ButtonsType.OK, _("Downloading keyboard file failed"))
dialog.run()
dialog.destroy()
return False
def _keyman_policy(self, web_view, decision, decision_type):

View file

@ -74,7 +74,10 @@ def get_keyboard_data(keyboardID, weekCache=False):
os.chdir(cache_dir)
requests_cache.install_cache(cache_name='keyman_cache', backend='sqlite', expire_after=expire_after)
now = time.ctime(int(time.time()))
response = requests.get(api_url)
try:
response = requests.get(api_url)
except requests.exceptions.RequestException as e: # This is the correct syntax
return None
logging.debug('Time: {0} / Used Cache: {1}'.format(now, response.from_cache))
os.chdir(current_dir)
requests_cache.uninstall_cache()

View file

@ -20,7 +20,6 @@ def download_and_install_package(url):
"""
parsedUrl = urlparse(url)
bcp47 = _extract_bcp47(parsedUrl.query)
severity = logging.ERROR
if parsedUrl.scheme == 'keyman':
logging.info("downloading " + url)
@ -49,7 +48,7 @@ def download_and_install_package(url):
return
if packageFile and not _install_package(packageFile, bcp47):
logging.log(severity, "Can't find file " + url)
logging.error("Can't find file " + url)
def _extract_bcp47(query):

View file

@ -74,13 +74,12 @@ class InstallKmp():
packageID, ext = os.path.splitext(os.path.basename(inputfile))
return packageID.lower()
def install_kmp_shared(self, inputfile, online=False, language=None):
def install_kmp_shared(self, inputfile, language=None):
"""
Install a kmp file to /usr/local/share/keyman
Args:
inputfile (str): path to kmp file
online (bool, default=False): whether to attempt to get online keyboard data
"""
self._check_keyman_dir(
'/usr/local/share',
@ -95,25 +94,25 @@ class InstallKmp():
_("You do not have permissions to install the font files to the shared font area "
"/usr/local/share/fonts"))
return self._install_kmp(inputfile, online, language, InstallLocation.Shared)
return self._install_kmp(inputfile, language, InstallLocation.Shared)
def install_kmp_user(self, inputfile, online=False, language=None):
return self._install_kmp(inputfile, online, language, InstallLocation.User)
def install_kmp_user(self, inputfile, language=None):
return self._install_kmp(inputfile, language, InstallLocation.User)
def _install_kmp(self, inputfile, online, language, area):
def _install_kmp(self, inputfile, language, area):
self.packageID = self._extract_package_id(inputfile)
self.packageDir = get_keyboard_dir(area, self.packageID)
self.kmpdocdir = get_keyman_doc_dir(area, self.packageID)
self.kmpfontdir = get_keyman_font_dir(area, self.packageID)
if not self._safeMakeDirs(self.packageDir):
return
if not os.path.isfile(inputfile):
message = _("File {kmpfile} doesn't exist").format(kmpfile=inputfile)
logging.error("install_kmp.py: %s", message)
raise InstallError(InstallStatus.Abort, message)
if not self._safeMakeDirs(self.packageDir):
return
extract_kmp(inputfile, self.packageDir)
if is_fcitx_running():
@ -136,11 +135,10 @@ class InstallKmp():
raise InstallError(InstallStatus.Abort, message)
if keyboards:
logging.info("Installing %s", secure_lookup(info, 'name', 'description'))
if online:
process_keyboard_data(self.packageID, self.packageDir)
for kb in keyboards:
if kb['id'] != self.packageID:
process_keyboard_data(kb['id'], self.packageDir)
process_keyboard_data(self.packageID, self.packageDir)
for kb in keyboards:
if kb['id'] != self.packageID:
process_keyboard_data(kb['id'], self.packageDir)
if files is None:
return self.install_keyboards(keyboards, self.packageDir, language)
@ -309,21 +307,20 @@ def process_keyboard_data(keyboardID, packageDir) -> None:
# raise InstallError(InstallStatus.Abort, message)
def install_kmp(inputfile, online=False, sharedarea=False, language=None):
def install_kmp(inputfile, sharedarea=False, language=None):
"""
Install a kmp file
Args:
inputfile (str): path to kmp file
online(bool, default=False): whether to attempt to get online keyboard data
sharedarea(bool, default=False): whether install kmp to shared area or user directory
language(str, default=None): language to install keyboard for
has_ui(bool, default=True): whether we're displaying a window or running UI less from the command line
"""
if sharedarea:
return_value = InstallKmp().install_kmp_shared(inputfile, online, language)
return_value = InstallKmp().install_kmp_shared(inputfile, language)
else:
return_value = InstallKmp().install_kmp_user(inputfile, online, language)
return_value = InstallKmp().install_kmp_user(inputfile, language)
get_keyman_config_service().keyboard_list_changed()
return return_value

View file

@ -47,10 +47,9 @@ def find_keyman_image(image_file):
class InstallKmpWindow(Gtk.Dialog):
def __init__(self, kmpfile, online=False, viewkmp=None, language=None):
def __init__(self, kmpfile, viewkmp=None, language=None):
logging.debug("InstallKmpWindow: kmpfile: %s", kmpfile)
self.kmpfile = kmpfile
self.online = online
self.viewwindow = viewkmp
self.accelerators = None
self.language = language
@ -327,7 +326,7 @@ class InstallKmpWindow(Gtk.Dialog):
def on_install_clicked(self, button):
logging.info("Installing keyboard")
try:
result = install_kmp(self.kmpfile, self.online, language=self.language)
result = install_kmp(self.kmpfile, language=self.language)
if result:
# If install_kmp returns a string, it is an instruction for the end user,
# because for fcitx they will need to take extra steps to complete

View file

@ -116,6 +116,7 @@ class KeyboardDetailsView(Gtk.Dialog):
lbl_pkg_desc = Gtk.Label()
lbl_pkg_desc.set_text(_("Package description: "))
lbl_pkg_desc.set_halign(Gtk.Align.END)
lbl_pkg_desc.set_valign(Gtk.Align.START)
grid.attach_next_to(lbl_pkg_desc, prevlabel, Gtk.PositionType.BOTTOM, 1, 1)
prevlabel = lbl_pkg_desc
label = Gtk.Label()
@ -125,28 +126,26 @@ class KeyboardDetailsView(Gtk.Dialog):
label.set_line_wrap(80)
grid.attach_next_to(label, lbl_pkg_desc, Gtk.PositionType.RIGHT, 1, 1)
if secure_lookup(info, "author"):
if secure_lookup(info, 'author', 'description'):
lbl_pkg_auth = Gtk.Label()
lbl_pkg_auth.set_text(_("Package author: "))
lbl_pkg_auth.set_halign(Gtk.Align.END)
grid.attach_next_to(lbl_pkg_auth, prevlabel, Gtk.PositionType.BOTTOM, 1, 1)
prevlabel = lbl_pkg_auth
label = Gtk.Label()
if secure_lookup(info, 'author', 'description'):
label.set_text(secure_lookup(info, 'author', 'description'))
label.set_text(secure_lookup(info, 'author', 'description'))
label.set_halign(Gtk.Align.START)
label.set_selectable(True)
grid.attach_next_to(label, lbl_pkg_auth, Gtk.PositionType.RIGHT, 1, 1)
if secure_lookup(info, "copyright"):
if secure_lookup(info, 'copyright', 'description'):
lbl_pkg_cpy = Gtk.Label()
lbl_pkg_cpy.set_text(_("Package copyright: "))
lbl_pkg_cpy.set_halign(Gtk.Align.END)
grid.attach_next_to(lbl_pkg_cpy, prevlabel, Gtk.PositionType.BOTTOM, 1, 1)
prevlabel = lbl_pkg_cpy
label = Gtk.Label()
if secure_lookup(info, 'copyright', 'description'):
label.set_text(secure_lookup(info, 'copyright', 'description'))
label.set_text(secure_lookup(info, 'copyright', 'description'))
label.set_halign(Gtk.Align.START)
label.set_selectable(True)
grid.attach_next_to(label, lbl_pkg_cpy, Gtk.PositionType.RIGHT, 1, 1)

View file

@ -506,7 +506,6 @@ def get_metadata(tmpdirname):
If it does not exist then will return get_and_convert_infdata
Args:
inputfile (str): path to kmp file
tmpdirname(str): temp directory to extract kmp
Returns:

View file

@ -37,31 +37,32 @@ def uninstall_kmp_shared(packageID):
"""
kbdir = get_keyboard_dir(InstallLocation.Shared, packageID)
if not os.path.isdir(kbdir):
logging.error("Keyboard directory for %s does not exist. Aborting", packageID)
exit(3)
msg = _("Keyboard directory for %s does not exist." % packageID)
logging.error(msg)
return msg
kbdocdir = get_keyman_doc_dir(InstallLocation.Shared, packageID)
kbfontdir = get_keyman_font_dir(InstallLocation.Shared, packageID)
logging.info("Uninstalling shared keyboard: %s", packageID)
if not os.access(kbdir, os.X_OK | os.W_OK): # Check for write access of keyman dir
logging.error(
"You do not have permissions to uninstall the keyboard files. You need to run this with `sudo`")
exit(3)
msg = _("You do not have permissions to uninstall the keyboard files. You need to run this with `sudo`")
logging.error(msg)
return msg
if os.path.isdir(kbdocdir):
if not os.access(kbdocdir, os.X_OK | os.W_OK): # Check for write access of keyman doc dir
logging.error(
"You do not have permissions to uninstall the documentation. You need to run this with `sudo`")
exit(3)
msg = _("You do not have permissions to uninstall the documentation. You need to run this with `sudo`")
logging.error(msg)
return msg
delete_dir(kbdocdir)
logging.info("Removed documentation directory: %s", kbdocdir)
else:
logging.info("No documentation directory")
if os.path.isdir(kbfontdir):
if not os.access(kbfontdir, os.X_OK | os.W_OK): # Check for write access of keyman fonts
logging.error(
"You do not have permissions to uninstall the font files. You need to run this with `sudo`")
exit(3)
msg = _("You do not have permissions to uninstall the font files. You need to run this with `sudo`")
logging.error(msg)
return msg
delete_dir(kbfontdir)
logging.info("Removed font directory: %s", kbfontdir)
else:
@ -80,6 +81,7 @@ def uninstall_kmp_shared(packageID):
delete_dir(kbdir)
logging.info("Removed keyman directory: %s", kbdir)
logging.info("Finished uninstalling shared keyboard: %s", packageID)
return ''
def uninstall_keyboards_from_ibus(keyboards, packageDir):
@ -130,8 +132,9 @@ def uninstall_kmp_user(packageID):
"""
kbdir = get_keyboard_dir(InstallLocation.User, packageID)
if not os.path.isdir(kbdir):
logging.error("Keyboard directory for %s does not exist. Aborting", packageID)
exit(3)
msg = _("Keyboard directory for %s does not exist." % packageID)
logging.error(msg)
return msg
logging.info("Uninstalling local keyboard: %s", packageID)
info, system, options, keyboards, files = get_metadata(kbdir)
if keyboards:
@ -150,6 +153,7 @@ def uninstall_kmp_user(packageID):
delete_dir(fontdir)
logging.info("Removed user keyman font directory: %s", fontdir)
logging.info("Finished uninstalling local keyboard: %s", packageID)
return ''
def uninstall_kmp(packageID, sharedarea=False):
@ -161,8 +165,9 @@ def uninstall_kmp(packageID, sharedarea=False):
sharedarea (str): whether to uninstall from shared /usr/local or ~/.local
"""
if sharedarea:
uninstall_kmp_shared(packageID)
msg = uninstall_kmp_shared(packageID)
else:
uninstall_kmp_user(packageID)
msg = uninstall_kmp_user(packageID)
get_keyman_config_service().keyboard_list_changed()
return msg

View file

@ -22,6 +22,7 @@ from keyman_config.get_kmp import (InstallLocation, get_keyboard_dir,
get_keyman_dir)
from keyman_config.install_window import InstallKmpWindow, find_keyman_image
from keyman_config.keyboard_details import KeyboardDetailsView
from keyman_config.kmpmetadata import get_fonts, parsemetadata
from keyman_config.list_installed_kmp import get_installed_kmp
from keyman_config.options import OptionsView
from keyman_config.uninstall_kmp import uninstall_kmp
@ -351,18 +352,40 @@ class ViewInstalledWindow(ViewInstalledWindowBase):
model, treeiter = self.tree.get_selection().get_selected()
if treeiter is not None:
logging.info("Uninstall keyboard " + model[treeiter][3] + "?")
dialog = Gtk.MessageDialog(
self, 0, Gtk.MessageType.QUESTION,
Gtk.ButtonsType.YES_NO, _("Uninstall keyboard package?"))
dialog.format_secondary_text(
_("Are you sure that you want to uninstall the {keyboard} keyboard and its fonts?")
.format(keyboard=model[treeiter][1]))
dialog = Gtk.MessageDialog(self, 0, Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO,
_("Uninstall keyboard package?"))
msg = _("Are you sure that you want to uninstall the {keyboard} keyboard?").format(
keyboard=model[treeiter][1])
kbdir = get_keyboard_dir(InstallLocation.User, model[treeiter][3])
kmpjson = os.path.join(kbdir, "kmp.json")
if os.path.isfile(kmpjson):
info, system, options, keyboards, files = parsemetadata(kmpjson, False)
fonts = get_fonts(files)
if fonts:
# Fonts are optional
fontlist = ""
for font in fonts:
if 'description' in font:
if fontlist != "":
fontlist = fontlist + "\n"
if font['description'][:5] == "Font ":
fontdesc = font['description'][5:]
else:
fontdesc = font['description']
fontlist = fontlist + fontdesc
msg += "\n\n" + _("The following fonts will also be uninstalled:\n") + fontlist
dialog.format_secondary_text(msg)
response = dialog.run()
dialog.destroy()
if response == Gtk.ResponseType.YES:
logging.info("Uninstalling keyboard" + model[treeiter][1])
# can only uninstall with the gui from user area
uninstall_kmp(model[treeiter][3])
msg = uninstall_kmp(model[treeiter][3])
if not msg == '':
md = Gtk.MessageDialog(self, 0, Gtk.MessageType.ERROR,
Gtk.ButtonsType.OK, _("Uninstalling keyboard failed.\n\nError message: ") + msg)
md.run()
md.destroy()
logging.info("need to restart window after uninstalling a keyboard")
self.restart()
elif response == Gtk.ResponseType.NO:

View file

@ -1,4 +1,4 @@
#/usr/bin/env bash
#!/usr/bin/env bash
_km-config_completions()
{
@ -9,14 +9,15 @@ _km-config_completions()
opts="-h --help -v --verbose -vv --veryverbose --version -i --install"
if [[ ${cur} == -* ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
# shellcheck disable=SC2207
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
# default IFS splits 'file://' into 'file', ':' and '//'
if [ "${prev}" == ":" -a "${COMP_WORDS[COMP_CWORD-2]}" == "file" ]; then
if [ "${prev}" == ":" ] && [ "${COMP_WORDS[COMP_CWORD-2]}" == "file" ]; then
local flag=${COMP_WORDS[COMP_CWORD-3]}
if [ $flag == "-i" -o $flag == "--install" ]; then
if [ "$flag" == "-i" ] || [ "$flag" == "--install" ]; then
prev=$flag
fi
fi
@ -25,7 +26,8 @@ _km-config_completions()
"-i"|"--install")
local IFS=$'\n'
compopt -o filenames
COMPREPLY=( $(compgen -f -X "!"*.kmp -- $cur) $(compgen -d -- $cur) )
# shellcheck disable=SC2207
COMPREPLY=( $(compgen -f -X "!"*.kmp -- "$cur") $(compgen -d -- "$cur") )
return 0
;;
*)

View file

@ -1,4 +1,4 @@
#/usr/bin/env bash
#!/usr/bin/env bash
_km-kvk2ldml_completions()
{
@ -9,7 +9,8 @@ _km-kvk2ldml_completions()
opts="-h --help -p --print -k --keys -o --output -v --verbose -vv --veryverbose --version"
if [[ ${cur} == -* ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
# shellcheck disable=SC2207
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
@ -17,7 +18,8 @@ _km-kvk2ldml_completions()
"-o"|"--output")
local IFS=$'\n'
compopt -o filenames
COMPREPLY=( $(compgen -f -- $cur) )
# shellcheck disable=SC2207
COMPREPLY=( $(compgen -f -- "$cur") )
return 0
;;
*)
@ -26,7 +28,8 @@ _km-kvk2ldml_completions()
local IFS=$'\n'
compopt -o filenames
COMPREPLY=( $(compgen -f -X "!"*.kvk -- $cur) $(compgen -d -- $cur) )
# shellcheck disable=SC2207
COMPREPLY=( $(compgen -f -X "!"*.kvk -- "$cur") $(compgen -d -- "$cur") )
}
complete -F _km-kvk2ldml_completions km-kvk2ldml

View file

@ -1,17 +1,17 @@
#/usr/bin/env bash
#!/usr/bin/env bash
_km-package-get_completions()
{
local cur prev opts cache
local cur opts cache pkg_install_path
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
opts="-h --help -v --verbose -vv --veryverbose --version"
cache=${XDG_CACHE_HOME:-~/.cache}/keyman/kmpdirlist
if [[ ${cur} == -* ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
# shellcheck disable=SC2207
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
@ -21,15 +21,17 @@ _km-package-get_completions()
# Unfortunately with bash completion scripts it's not possible to factor out
# common code.
if [[ -e ./km-package-install ]]; then
python3 -c "from importlib.machinery import SourceFileLoader;from importlib.util import module_from_spec, spec_from_loader;loader = SourceFileLoader('km_package_install', './km-package-install');spec = spec_from_loader(loader.name, loader);mod = module_from_spec(spec);loader.exec_module(mod);mod.list_keyboards()"
pkg_install_path='./km-package-install'
else
python3 -c "from importlib.machinery import SourceFileLoader;from importlib.util import module_from_spec, spec_from_loader;loader = SourceFileLoader('km_package_install', '/usr/bin/km-package-install');spec = spec_from_loader(loader.name, loader);mod = module_from_spec(spec);loader.exec_module(mod);mod.list_keyboards()"
pkg_install_path='/usr/bin/km-package-install'
fi
python3 -c "from importlib.machinery import SourceFileLoader;from importlib.util import module_from_spec, spec_from_loader;loader = SourceFileLoader('km_package_install', $pkg_install_path);spec = spec_from_loader(loader.name, loader);mod = module_from_spec(spec);loader.exec_module(mod);mod.list_keyboards()"
fi
if [[ -r $cache ]] ; then
for file in `cat $cache`; do words="${words} ${file}"; done
COMPREPLY=($(compgen -W "${words}" -- ${cur}))
while read -r file; do words="${words} ${file}"; done < "$cache"
# shellcheck disable=SC2207
COMPREPLY=($(compgen -W "${words}" -- "${cur}"))
return 0
fi
}

View file

@ -10,6 +10,8 @@ from pkg_resources import parse_version
from zipfile import is_zipfile
from keyman_config import KeymanApiUrl, __version__, secure_lookup
from keyman_config.install_kmp import extract_kmp
from keyman_config.kmpmetadata import get_metadata
from keyman_config.uninstall_kmp import uninstall_kmp
@ -84,6 +86,33 @@ def list_keyboards():
write_kmpdirlist(kmpdirfile)
def _list_languages_for_keyboard_impl(packageId, packageDir):
from keyman_config.get_kmp import keyman_cache_dir
kmpfile = os.path.join(keyman_cache_dir(), packageId)
if not os.path.exists(kmpfile):
kmpfile = kmpfile + '.kmp'
if not os.path.exists(kmpfile):
return ''
extract_kmp(kmpfile, packageDir)
info, system, options, keyboards, files = get_metadata(packageDir)
if not keyboards:
return ''
firstKeyboard = keyboards[0]
if not secure_lookup(firstKeyboard, 'languages') or len(firstKeyboard['languages']) <= 0:
return ''
result = ''
for lang in firstKeyboard['languages']:
if result:
result += '\n'
result += lang['id']
return result
def list_languages_for_keyboard(packageId, packageDir):
result = _list_languages_for_keyboard_impl(packageId, packageDir)
print(result)
def main():
parser = argparse.ArgumentParser(
description='Install a Keyman keyboard package, either a local .kmp file or specify a ' +
@ -122,9 +151,9 @@ def main():
if os.path.exists(os.path.join(keyman_cache_dir(), 'kmpdirlist')):
os.remove(os.path.join(keyman_cache_dir(), 'kmpdirlist'))
def try_install_kmp(inputfile, arg, language=None, online=False, sharedarea=False):
def try_install_kmp(inputfile, arg, language=None, sharedarea=False):
try:
install_kmp(inputfile, online, sharedarea, language)
install_kmp(inputfile, sharedarea, language)
except InstallError as e:
if e.status == InstallStatus.Abort:
logging.error("km-package-install: error: Failed to install %s", arg)
@ -144,7 +173,7 @@ def main():
logging.error("km-package-install: Keyman kmp file %s not found.", args.file)
logging.error("km-package-install -f <kmpfile>")
sys.exit(2)
try_install_kmp(args.file, "file " + args.file, args.bcp47, False, args.shared)
try_install_kmp(args.file, "file " + args.file, args.bcp47, args.shared)
elif args.package:
if args.shared:
if get_kmp_version_user(args.package):
@ -176,7 +205,7 @@ def main():
kmpfile = get_kmp(args.package)
if kmpfile:
try_install_kmp(kmpfile, "keyboard package " + args.package, args.bcp47, True, args.shared)
try_install_kmp(kmpfile, "keyboard package " + args.package, args.bcp47, args.shared)
else:
logging.error("km-package-install: error: Could not download keyboard package %s", args.package)
sys.exit(2)

View file

@ -1,20 +1,29 @@
#/usr/bin/env bash
#!/usr/bin/env bash
_km-package-install_completions()
{
local cur prev opts cache
local cur prev opts cache pkg_install_path
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
opts="-h --help -v --verbose -vv --veryverbose --version -p --package -f --file -s --shared"
opts="-h --help -v --verbose -vv --veryverbose --version -p --package -f --file -s --shared -l --bcp47"
cache=${XDG_CACHE_HOME:-~/.cache}/keyman/kmpdirlist
if [[ ${cur} == -* ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
# shellcheck disable=SC2207
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
if [[ -e ./km-package-install ]]; then
pkg_install_path='./km-package-install'
get_kmp_path='keyman_config/get_kmp.py'
else
pkg_install_path='/usr/bin/km-package-install'
get_kmp_path='/usr/lib/python3/dist-packages/keyman_config/get_kmp.py'
fi
case "${prev}" in
"-p"|"--package")
words=""
@ -22,23 +31,58 @@ _km-package-install_completions()
# NOTE: identical code in `km-package-get.bash-completion`.
# Unfortunately with bash completion scripts it's not possible to factor out
# common code.
if [[ -e ./km-package-install ]]; then
python3 -c "from importlib.machinery import SourceFileLoader;from importlib.util import module_from_spec, spec_from_loader;loader = SourceFileLoader('km_package_install', './km-package-install');spec = spec_from_loader(loader.name, loader);mod = module_from_spec(spec);loader.exec_module(mod);mod.list_keyboards()"
else
python3 -c "from importlib.machinery import SourceFileLoader;from importlib.util import module_from_spec, spec_from_loader;loader = SourceFileLoader('km_package_install', '/usr/bin/km-package-install');spec = spec_from_loader(loader.name, loader);mod = module_from_spec(spec);loader.exec_module(mod);mod.list_keyboards()"
fi
python3 -c "from importlib.machinery import SourceFileLoader;from importlib.util import module_from_spec, spec_from_loader;loader = SourceFileLoader('km_package_install', '$pkg_install_path');spec = spec_from_loader(loader.name, loader);mod = module_from_spec(spec);loader.exec_module(mod);mod.list_keyboards()"
fi
if [[ -r $cache ]] ; then
for file in `cat $cache`; do words="${words} ${file}"; done
COMPREPLY=($(compgen -W "${words}" -- ${cur}))
while read -r file; do words="${words} ${file}"; done < "$cache"
# shellcheck disable=SC2207
COMPREPLY=($(compgen -W "${words}" -- "${cur}"))
return 0
fi
;;
"-f"|"--file")
local IFS=$'\n'
compopt -o filenames
COMPREPLY=( $(compgen -f -X "!"*.kmp -- $cur) $(compgen -d -- $cur) )
# shellcheck disable=SC2207
COMPREPLY=( $(compgen -f -X "!"*.kmp -- "$cur") $(compgen -d -- "$cur") )
return 0
;;
"-l"|"--bcp47")
local packageId=""
local packageDir
packageDir=$(mktemp -d)
for ((i=0;i<$COMP_CWORD;i++)); do
case ${COMP_WORDS[$i]} in
"-p"|"--package")
packageId="${COMP_WORDS[$i+1]}"
# shellcheck disable=SC2086 # doesn't work with quotes
if [ ! -f ${XDG_CACHE_HOME:-~/.cache}/keyman/$packageId ] && [ ! -f ${XDG_CACHE_HOME:-~/.cache}/keyman/${packageId}.kmp ]; then
python3 -c "from importlib.machinery import SourceFileLoader;from importlib.util import module_from_spec, spec_from_loader;loader = SourceFileLoader('get_kmp', '$get_kmp_path');spec = spec_from_loader(loader.name, loader);mod = module_from_spec(spec);loader.exec_module(mod);mod.get_kmp('$packageId')"
fi
break
;;
"-f"|"--file")
package="${COMP_WORDS[$i+1]}"
packageId=$(basename "$package")
# shellcheck disable=SC2086 # doesn't work with quotes
cp "$package" ${XDG_CACHE_HOME:-~/.cache}/keyman/${packageId}
break
;;
*)
;;
esac
done
if [ -z "$packageId" ]; then
return 0
fi
words=""
while read -r lang; do
words="$words $lang"
done < <(python3 -c "from importlib.machinery import SourceFileLoader;from importlib.util import module_from_spec, spec_from_loader;loader = SourceFileLoader('km_package_install', '$pkg_install_path');spec = spec_from_loader(loader.name, loader);mod = module_from_spec(spec);loader.exec_module(mod);mod.list_languages_for_keyboard('$packageId', '$packageDir')")
# shellcheck disable=SC2207
COMPREPLY=($(compgen -W "${words}" -- "${cur}"))
return 0
;;
*)

View file

@ -1,15 +1,15 @@
#/usr/bin/env bash
#!/usr/bin/env bash
_km-package-list-installed_completions()
{
local cur prev opts
local cur opts
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
opts="-h --help -l --long -v --verbose -vv --veryverbose --version -u --user -o --os -s --shared"
if [[ ${cur} == -* ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
# shellcheck disable=SC2207
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
}

View file

@ -1,4 +1,4 @@
#/usr/bin/env bash
#!/usr/bin/env bash
_km-package-uninstall_completions()
{
@ -9,7 +9,8 @@ _km-package-uninstall_completions()
opts="-h --help -s --shared -v --verbose -vv --veryverbose --version"
if [[ ${cur} == -* ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
# shellcheck disable=SC2207
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
@ -23,12 +24,15 @@ _km-package-uninstall_completions()
shared=""
case "${prev}" in
"-s"|"--shared")
for file in $(ls -1 /usr/local/share/keyman); do kbid="`basename ${file}`"; shared="${shared} ${kbid}"; done
COMPREPLY=($(compgen -W "${shared}" -- ${cur}))
for file in /usr/local/share/keyman/*; do kbid="$(basename "${file}")"; shared="${shared} ${kbid}"; done
# shellcheck disable=SC2207
COMPREPLY=($(compgen -W "${shared}" -- "${cur}"))
;;
*)
for file in $(ls -1 ${XDG_DATA_HOME:-~/.local/share}/keyman); do kbid="`basename ${file}`"; words="${words} ${kbid}"; done
COMPREPLY=($(compgen -W "${words}" -- ${cur}))
# shellcheck disable=SC2231 # doesn't work with quotes
for file in ${XDG_DATA_HOME:-~/.local/share}/keyman/*; do kbid="$(basename "${file}")"; words="${words} ${kbid}"; done
# shellcheck disable=SC2207
COMPREPLY=($(compgen -W "${words}" -- "${cur}"))
;;
esac
}

View file

@ -272,7 +272,7 @@ class InstallKmpTests(unittest.TestCase):
# Execute
with self.assertRaises(InstallError) as context:
InstallKmp()._install_kmp(kmpfile, False, 'km', InstallLocation.User)
InstallKmp()._install_kmp(kmpfile, 'km', InstallLocation.User)
# Verify
self.assertTrue('foo.kmp requires Keyman 99.0 or higher' in context.exception.message)
@ -296,7 +296,7 @@ class InstallKmpTests(unittest.TestCase):
self._createKmpJson(packagedir, testcase['fileVersion'])
# Execute
InstallKmp()._install_kmp(kmpfile, False, 'km', InstallLocation.User)
InstallKmp()._install_kmp(kmpfile, 'km', InstallLocation.User)
# Verify
self.mockInstallToIbus.assert_called_once()

View file

@ -0,0 +1,60 @@
#!/usr/bin/python3
import os
import tempfile
import unittest
from unittest.mock import patch, ANY
from importlib.machinery import SourceFileLoader
from importlib.util import module_from_spec, spec_from_loader
class PackageInstallCompletionTests(unittest.TestCase):
def setUp(self):
patcher1 = patch('keyman_config.install_kmp.extract_kmp')
self.mockExtractKmp = patcher1.start()
self.addCleanup(patcher1.stop)
patcher2 = patch('keyman_config.kmpmetadata.get_metadata')
self.mockGetMetadata = patcher2.start()
self.addCleanup(patcher2.stop)
loader = SourceFileLoader('km_package_install', os.path.join(os.path.dirname(
os.path.abspath(__file__)), '../km-package-install'))
spec = spec_from_loader(loader.name, loader)
if spec:
self.mod = module_from_spec(spec)
loader.exec_module(self.mod)
self.tempDir = tempfile.TemporaryDirectory()
os.environ["XDG_CACHE_HOME"] = self.tempDir.name
self.cacheDir = os.path.join(self.tempDir.name, 'keyman')
os.makedirs(self.cacheDir)
def _list_languages_for_keyboard_impl(self, packageId):
return self.mod._list_languages_for_keyboard_impl(packageId, 'someDir')
def test_PackageCompletionNoLanguage(self):
open(os.path.join(self.cacheDir, 'foo'), 'w')
self.mockGetMetadata.return_value = (None, None, None, [{}], None)
result = self._list_languages_for_keyboard_impl('foo')
self.assertEqual(result, "")
def test_PackageCompletionOneLanguage(self):
open(os.path.join(self.cacheDir, 'khmer_angkor'), 'w')
self.mockGetMetadata.return_value = (
None, None, None,
[{'languages': [{'name': 'Central Khmer (Khmer, Cambodia)', 'id': 'km'}]}],
None)
result = self._list_languages_for_keyboard_impl('khmer_angkor')
self.assertEqual(result, "km")
def test_PackageCompletionMultipleLanguages(self):
open(os.path.join(self.cacheDir, 'sil_euro_latin'), 'w')
self.mockGetMetadata.return_value = (
None, None, None,
[{'languages': [
{'name': 'English', 'id': 'en'},
{'name': 'French', 'id': 'fr'},
{'name': 'German', 'id': 'de'}]}],
None)
result = self._list_languages_for_keyboard_impl('sil_euro_latin')
self.assertEqual(result, 'en\nfr\nde')

View file

@ -158,7 +158,7 @@ NSString* _keymanDataPath = nil;
[[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"NSApplicationCrashOnExceptions": @YES }];
KeymanVersionInfo keymanVersionInfo = [self versionInfo];
NSString *releaseName = [NSString stringWithFormat:@"release-%@", keymanVersionInfo.versionWithTag];
NSString *releaseName = [NSString stringWithFormat:@"release@%@", keymanVersionInfo.versionWithTag];
[SentrySDK startWithConfigureOptions:^(SentryOptions *options) {
options.dsn = @"https://960f8b8e574c46e3be385d60ce8e1fea@o1005580.ingest.sentry.io/5983522";

View file

@ -40,7 +40,7 @@ public class MainActivity extends AppCompatActivity implements OnKeyboardDownloa
context = this;
SentryAndroid.init(context, options -> {
options.setRelease("release-"+com.firstvoices.keyboards.BuildConfig.VERSION_NAME);
options.setRelease("release@"+com.firstvoices.keyboards.BuildConfig.VERSION_NAME);
options.setEnvironment(com.firstvoices.keyboards.BuildConfig.VERSION_ENVIRONMENT);
});

View file

@ -1,5 +1,4 @@
github "marmelroy/Zip"
github "DaveWoodCom/XCGLogger" ~> 6.1.0
github "devicekit/DeviceKit" ~> 2.3
github "devicekit/DeviceKit" ~> 5.0
github "ashleymills/Reachability.swift"
github "getsentry/sentry-cocoa" ~> 6.2.1

View file

@ -1,5 +1,4 @@
github "DaveWoodCom/XCGLogger" "6.1.0"
github "ashleymills/Reachability.swift" "v5.0.0"
github "devicekit/DeviceKit" "2.3.0"
github "ashleymills/Reachability.swift" "v5.1.0"
github "devicekit/DeviceKit" "5.0.0"
github "getsentry/sentry-cocoa" "6.2.1"
github "marmelroy/Zip" "2.0.0"

View file

@ -57,15 +57,12 @@
CEBD34232654D76B00EB2EA8 /* Sentry.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34092654D5AA00EB2EA8 /* Sentry.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
CEBD34252654D76C00EB2EA8 /* XCGLogger.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD340A2654D5AA00EB2EA8 /* XCGLogger.xcframework */; };
CEBD34262654D76C00EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD340A2654D5AA00EB2EA8 /* XCGLogger.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
CEBD34272654D76E00EB2EA8 /* Zip.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD340B2654D5AA00EB2EA8 /* Zip.xcframework */; };
CEBD34282654D76E00EB2EA8 /* Zip.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD340B2654D5AA00EB2EA8 /* Zip.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
CEBD34472654FF3300EB2EA8 /* KeymanEngine.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34012654D41200EB2EA8 /* KeymanEngine.xcframework */; };
CEDB327F265C9C58000A2009 /* DeviceKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34052654D4EB00EB2EA8 /* DeviceKit.xcframework */; };
CEDB3280265C9C58000A2009 /* ObjcExceptionBridging.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34082654D5AA00EB2EA8 /* ObjcExceptionBridging.xcframework */; };
CEDB3281265C9C58000A2009 /* Reachability.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD340C2654D5AB00EB2EA8 /* Reachability.xcframework */; };
CEDB3282265C9C58000A2009 /* Sentry.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34092654D5AA00EB2EA8 /* Sentry.xcframework */; };
CEDB3283265C9C58000A2009 /* XCGLogger.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD340A2654D5AA00EB2EA8 /* XCGLogger.xcframework */; };
CEDB3284265C9C58000A2009 /* Zip.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD340B2654D5AA00EB2EA8 /* Zip.xcframework */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@ -81,7 +78,6 @@
CEBD34232654D76B00EB2EA8 /* Sentry.xcframework in Embed Frameworks */,
CEBD34262654D76C00EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */,
CEBD34032654D41200EB2EA8 /* KeymanEngine.xcframework in Embed Frameworks */,
CEBD34282654D76E00EB2EA8 /* Zip.xcframework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
@ -147,7 +143,6 @@
CEBD34082654D5AA00EB2EA8 /* ObjcExceptionBridging.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ObjcExceptionBridging.xcframework; path = Carthage/Build/ObjcExceptionBridging.xcframework; sourceTree = "<group>"; };
CEBD34092654D5AA00EB2EA8 /* Sentry.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Sentry.xcframework; path = Carthage/Build/Sentry.xcframework; sourceTree = "<group>"; };
CEBD340A2654D5AA00EB2EA8 /* XCGLogger.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = XCGLogger.xcframework; path = Carthage/Build/XCGLogger.xcframework; sourceTree = "<group>"; };
CEBD340B2654D5AA00EB2EA8 /* Zip.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Zip.xcframework; path = Carthage/Build/Zip.xcframework; sourceTree = "<group>"; };
CEBD340C2654D5AB00EB2EA8 /* Reachability.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Reachability.xcframework; path = Carthage/Build/Reachability.xcframework; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -162,7 +157,6 @@
CEBD341A2654D76600EB2EA8 /* DeviceKit.xcframework in Frameworks */,
CEBD34022654D41200EB2EA8 /* KeymanEngine.xcframework in Frameworks */,
CEBD34252654D76C00EB2EA8 /* XCGLogger.xcframework in Frameworks */,
CEBD34272654D76E00EB2EA8 /* Zip.xcframework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -175,7 +169,6 @@
CEDB3281265C9C58000A2009 /* Reachability.xcframework in Frameworks */,
CEDB3282265C9C58000A2009 /* Sentry.xcframework in Frameworks */,
CEDB3283265C9C58000A2009 /* XCGLogger.xcframework in Frameworks */,
CEDB3284265C9C58000A2009 /* Zip.xcframework in Frameworks */,
CEBD34472654FF3300EB2EA8 /* KeymanEngine.xcframework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -295,7 +288,6 @@
CEBD340C2654D5AB00EB2EA8 /* Reachability.xcframework */,
CEBD34092654D5AA00EB2EA8 /* Sentry.xcframework */,
CEBD340A2654D5AA00EB2EA8 /* XCGLogger.xcframework */,
CEBD340B2654D5AA00EB2EA8 /* Zip.xcframework */,
CEBD34052654D4EB00EB2EA8 /* DeviceKit.xcframework */,
CEBD34012654D41200EB2EA8 /* KeymanEngine.xcframework */,
);
@ -590,7 +582,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = "";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -642,7 +634,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = "";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
@ -662,7 +654,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = D7TR486TEH;
INFOPLIST_FILE = FirstVoices/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
KEYMAN_ROOT = "$(SRCROOT)/../../..";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -691,7 +683,7 @@
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = D7TR486TEH;
INFOPLIST_FILE = FirstVoices/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
KEYMAN_ROOT = "$(SRCROOT)/../../..";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -718,7 +710,7 @@
DEVELOPMENT_TEAM = D7TR486TEH;
HEADER_SEARCH_PATHS = "";
INFOPLIST_FILE = SWKeyboard/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
KEYMAN_ROOT = "$(SRCROOT)/../../..";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -749,7 +741,7 @@
DEVELOPMENT_TEAM = D7TR486TEH;
HEADER_SEARCH_PATHS = "";
INFOPLIST_FILE = SWKeyboard/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
KEYMAN_ROOT = "$(SRCROOT)/../../..";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",

View file

@ -165,12 +165,6 @@ if [ $DO_CARTHAGE = true ]; then
carthage checkout || fail "Carthage dependency loading failed"
# Carthage sometimes picks the wrong .xcworkspace if two are available in a dependency's repo.
# Easiest way to override it - delete the wrong one (or just its scheme)
# Deleted workspace - a test for proper deployment to CocoaPods. Doesn't matter here.
rm -r ./Carthage/Checkouts/DeviceKit/CocoaPodsVerification/ || fail "Carthage dependency loading failed"
# --no-use-binaries: due to https://github.com/Carthage/Carthage/issues/3134,
# which affects the sentry-cocoa dependency.
carthage build --use-xcframeworks --no-use-binaries --platform iOS || fail "Carthage dependency loading failed"

View file

@ -10,7 +10,7 @@
Sentry.init({
dsn: 'https://92eb58e6005d47daa33c9c9e39458eb7@o1005580.ingest.sentry.io/5983518', // Keyman for Windows
environment: '$Environment',
release: 'release-$VersionWithTag'
release: 'release@$VersionWithTag'
});
function keymanEnableDiagnostics() {

View file

@ -126,7 +126,7 @@ if [ "$action" == "commit" ]; then
pushd "$KEYMAN_ROOT" > /dev/null
message="auto: increment $base version to $NEWVERSION"
branch="auto/version-$base-$NEWVERSION"
git tag -a "release-$VERSION_WITH_TAG" -m "Keyman release $VERSION_WITH_TAG"
git tag -a "release@$VERSION_WITH_TAG" -m "Keyman release $VERSION_WITH_TAG"
git checkout -b "$branch"
git add VERSION.md HISTORY.md

View file

@ -210,7 +210,7 @@ INTERMEDIATE="../intermediate"
SOURCE="."
NODE_SOURCE="source"
SENTRY_RELEASE_VERSION="release-$VERSION_WITH_TAG"
SENTRY_RELEASE_VERSION="release@$VERSION_WITH_TAG"
readonly WEB_OUTPUT
readonly EMBED_OUTPUT

View file

@ -31,7 +31,7 @@ cd "$KEYMAN_ROOT/windows/src"
echo "Uploading symbols for desktop/"
sentry-cli upload-dif -p keyman-windows -t breakpad -t pdb desktop --include-sources
sentry-cli releases -p keyman-windows files "release-$VERSION_WITH_TAG" upload-sourcemaps desktop/kmshell/xml
sentry-cli releases -p keyman-windows files "release@$VERSION_WITH_TAG" upload-sourcemaps desktop/kmshell/xml
echo "Uploading symbols for engine/"
sentry-cli upload-dif -p keyman-windows -t breakpad -t pdb engine --include-sources

View file

@ -10,7 +10,7 @@ latest version of Keyman.
Keyman is a keyboard application that makes it easy for you to type in
your language in all your favourite Windows programs and across the Web.
Keyman is used by more than a million people to type in over 1000
Keyman is used by more than a million people to type in over 2000
languages and counting.
Keyman remaps your hardware keyboard to any one of hundreds of virtual

View file

@ -40,18 +40,44 @@ Keyman Configuration, see: [Keyboard Task - Enable or Disable a Keyboard](../ena
## Setting a Hotkey
To set a hotkey for a Keyman keyboard, use the [Hotkeys tab](hotkeys).
The Hotkey for selecting a keyboard will appear to the right of the keyboard name.
If the keyboard has more than one language associated with it then the hotkey will
appear beside the language in the keyboards details list.
1. Click the hotkey link beside the keyboard or language. The
Change Hotkey dialog box will be displayed.
![](../../desktop_images/hotkeys-change.png)
2. Select a default hotkey or click Custom and type the hotkey you wish
to use.
**Note:** If you press a single letter or Shift plus a single letter, the
hotkey will default to <kbd>Ctrl</kbd> + <kbd>Alt</kbd> plus the letter you pressed, in
order to avoid conflicts with standard keyboard input.
**Note:** To clear a hotkey that has been set previously, click Clear Hotkey
or press Backspace.
**Note:** Be aware that you can set the hotkey to replace common Windows hotkeys (
<kbd>Ctrl</kbd> + <kbd>C</kbd>, <kbd>Ctrl</kbd> + <kbd>V</kbd>, etc). This is not
recommended.
3. Click OK to save your selection.
The new hotkey will now be available.
The hotkeys can also be set for a Keyman keyboard, using the [Hotkeys tab](hotkeys).
## Showing Introductory Help
To show introductory help for a Keyman keyboard from the Keyboard
Layouts tab of Keyman Configuration:
1. Find a keyboard in the keyboard list.
1. Select the keyboard name from the keyboard list to expand options associated with it.
2. Click on the question mark icon
(![](../../desktop_images/icon-introductory-help.png)) beside the keyboard
name. The keyboard help documentation will be shown.
2. Click on the help button and the help documentation will be shown.
![](../../desktop_images/tab-layout-help.png)
## Viewing Keyboard Details
@ -60,23 +86,32 @@ Keyman Configuration:
1. Find a keyboard in the keyboard list.
2. Click on the down arrow
(![](../../desktop_images/keyboards-downarrow.png)) beside the keyboard
name. The keyboard information window will expand.
2. Select the keyboard name from the keyboard list to expand options associated with it.
The keyboard information window will expand.
![](../../desktop_images/tab-layout-detail1.png)
The initial drop down has the following information if available:
![](../../desktop_images/keyboards-hotkey.png)
- Languages associated with the keyboard
- Keyboard package name
- Keyboard package version number
- Copyright details
3. In the information window you can view keyboard details, including:
3. Click on the down arrow
![](../../desktop_images/keyboards-downarrow.png), at the end of the list will reveal
further details including:
- Keyboard version number
- Keyboard filename.
- Keyboard package name.
- Keyboard version number.
- Keyboard encodings.
- Keyboard layout type.
- On Screen Keyboard status.
- Documentation status.
- Copyright details.
- Installation details.
- Keyboard included fonts
- Keyboard encodings
- Keyboard layout type
- On Screen Keyboard status
- Documentation status
- Keyboard Message
![](../../desktop_images/tab-layout-detail2.png)
## Sharing a keyboard with other users and devices
@ -85,7 +120,7 @@ then you can share the keyboard with other devices and users using a QR
Code.
1. Expand the keyboard details for the keyboard you wish to share, and
click Share keyboard.
click the Share keyboard button.
2. A popup will appear with a QR Code. This QR Code can be scanned with
a mobile phone camera to automatically open a web page with a

View file

@ -33,12 +33,17 @@ To disable a Keyman keyboard:
4. Select the Keyboard Layouts tab.
5. Untick the checkbox beside the Keyman keyboard.
5. Select the keyboard name you want to disable to expand options associated with it.
6. Click OK.
6. Click on the Disable button.
When a keyboard layout is disabled, its name appears in grey and its
checkbox is unticked.
![](../desktop_images/tab-layout-disable.png)
7. Click OK.
When a keyboard layout is disabled, its name and details appear in grey and the disable
button now says enable. The Add/remove language... button is also disabled as this option is
not available while the keyboard is disabled.
## Enabling a Keyman keyboard
@ -57,9 +62,13 @@ To enable a Keyman keyboard:
4. Select the Keyboard Layouts tab.
5. Tick the checkbox beside the Keyman keyboard.
5. Select the keyboard name you want to enable to expand options associated with it.
6. Click OK.
6. Click on the Enable button.
![](../desktop_images/tab-layout-enable.png)
7. Click OK.
## Related Topics

View file

@ -23,13 +23,15 @@ To uninstall a Keyman keyboard:
![](../desktop_images/tab-layout.png)
5. Click on the uninstall icon ![](../desktop_images/icon-uninstall.png)
for the keyboard you want to uninstall.
5. Click on the keyboard name for the keyboard you want to uninstall to
expand options associated with it.
6. A message box is displayed asking you to confirm this is the package
or keyboard layout that you wish to uninstall. Click OK.
6. Click the Uninstall button.
7. Click OK.
7. A message box is displayed asking you to confirm this is the package
or keyboard layout that you wish to uninstall.
8. Click OK.
The Keyman keyboard is now removed from Keyman.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View file

@ -30,7 +30,7 @@ Correctly associating your Keyman keyboard has three main benefits.
right-to-left languages like Arabic, Hebrew, and Farsi.
- Applications will know how characters and symbols should be
rendered. This is partucularly necessary for many Indian
rendered. This is particularly necessary for many Indian
languages.
- Applications will know which language tools to use when spell
@ -49,21 +49,24 @@ Correctly associating your Keyman keyboard has three main benefits.
![](../desktop_images/tab-keyboards.png)
4. Click on the down arrow
(![](../desktop_images/keyboards-downarrow.png)) beside the keyboard
name.
4. Select the keyboard name to expand the options
5. Click the Add language button on the left side under 'Languages:'
5. Click the Add/remove language... button
![](../desktop_images/tab-layout.png)
![](../desktop_images/tab-add-lang.png)
6. From the pop up window, search for the Windows langauge you would
6. Click the Add button on the first pop-up
![](../desktop_images/tab-add-popup.png)
7. From the pop up window, search for the Windows language you would
like to use and click OK
![](../desktop_images/language-association.png)
7. To remove an association, hover over the language name, and click
the ![](../desktop_images/icon-uninstall.png) button that appears.
8. To remove an association, click
the ![](../desktop_images/icon-uninstall.png) button to the left of the language.
## Related Topics

View file

@ -129,9 +129,11 @@ implementation
uses
System.Types,
Vcl.Themes,
Sentry.Client,
Keyman.Configuration.UI.MitigationForWin10_1803,
Keyman.System.LanguageCodeUtils,
Keyman.System.KeymanSentryClient,
Keyman.Configuration.System.TIPMaintenance,
Keyman.UI.UfrmProgress,
MessageIdentifierConsts,
@ -147,6 +149,9 @@ uses
{ TfrmInstallKeyboardLanguage }
function InstallKeyboardLanguage(Owner: TForm; const KeyboardID, ISOCode: string; Silent: Boolean): Boolean;
var
n: Integer;
kbd: IKeymanKeyboardInstalled;
begin
Result := TTIPMaintenance.DoInstall(KeyboardID, ISOCode);
if not Result then
@ -157,7 +162,18 @@ begin
end
else
CheckForMitigationWarningFor_Win10_1803(Silent, '');
// Enable the keyboard
n := kmcom.Keyboards.IndexOf(KeyboardID);
if n < 0 then
begin
// The Keyboard was successully installed for the BCP47Code
// for some reason the index look up has failed. Still pass through
// the DoInstall reasult, however the keyboard will not be enabled.
TKeymanSentryClient.Client.MessageEvent(Sentry.Client.SENTRY_LEVEL_ERROR, 'InstallKeyboardLanguage: KeyboardID "'+KeyboardID+'" not found, attempting to install for language "'+ISOCode+'".');
Exit;
end;
kbd := kmcom.Keyboards[n];
kbd.Loaded := True;
kmcom.Apply;
Result := True;
end;

View file

@ -10,7 +10,7 @@
Sentry.init({
dsn: 'https://92eb58e6005d47daa33c9c9e39458eb7@o1005580.ingest.sentry.io/5983518', // Keyman for Windows
environment: '$Environment',
release: 'release-$VersionWithTag'
release: 'release@$VersionWithTag'
});
function keymanEnableDiagnostics() {