diff --git a/HISTORY.md b/HISTORY.md index eec093d169..c91a5ce189 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -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) diff --git a/VERSION.md b/VERSION.md index 0269c93878..f57e95f640 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -17.0.24 \ No newline at end of file +17.0.34 diff --git a/android/KMAPro/build.sh b/android/KMAPro/build.sh index 661703485f..a319db957d 100755 --- a/android/KMAPro/build.sh +++ b/android/KMAPro/build.sh @@ -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 ./ diff --git a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java index e186654064..8e443de85a 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java +++ b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java @@ -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); }); } diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java index 9354b48d9b..c7d3c4eedf 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java @@ -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); }); } diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/BaseActivity.java b/android/KMEA/app/src/main/java/com/keyman/engine/BaseActivity.java index acba216e57..261cd950b8 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/BaseActivity.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/BaseActivity.java @@ -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 diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java index 9b779f942d..26d6285ff0 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java @@ -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. diff --git a/common/web/keyman-version/build.sh b/common/web/keyman-version/build.sh index 31774b007b..ad6b6b5162 100755 --- a/common/web/keyman-version/build.sh +++ b/common/web/keyman-version/build.sh @@ -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 diff --git a/common/windows/cpp/src/keymansentry.cpp b/common/windows/cpp/src/keymansentry.cpp index 1de659b489..37eabb51d4 100644 --- a/common/windows/cpp/src/keymansentry.cpp +++ b/common/windows/cpp/src/keymansentry.cpp @@ -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 diff --git a/common/windows/delphi/general/Keyman.System.KeymanSentryClient.pas b/common/windows/delphi/general/Keyman.System.KeymanSentryClient.pas index e24310f2cf..21256605e5 100644 --- a/common/windows/delphi/general/Keyman.System.KeymanSentryClient.pas +++ b/common/windows/delphi/general/Keyman.System.KeymanSentryClient.pas @@ -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 diff --git a/core/src/kmx/kmx_debug.cpp b/core/src/kmx/kmx_debug.cpp index a820c6bc45..fcff8f272d 100644 --- a/core/src/kmx/kmx_debug.cpp +++ b/core/src/kmx/kmx_debug.cpp @@ -14,6 +14,11 @@ using namespace kmx; #include +/** + * \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]; } diff --git a/developer/src/server/src/index.ts b/developer/src/server/src/index.ts index 2f73e336df..a26f25ccf7 100644 --- a/developer/src/server/src/index.ts +++ b/developer/src/server/src/index.ts @@ -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'); diff --git a/developer/src/server/src/site/lib/sentry/init.js.in b/developer/src/server/src/site/lib/sentry/init.js.in index 38371eb951..822647ad70 100644 --- a/developer/src/server/src/site/lib/sentry/init.js.in +++ b/developer/src/server/src/site/lib/sentry/init.js.in @@ -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() { diff --git a/developer/src/tike/xml/app/lib/sentry/init.js.in b/developer/src/tike/xml/app/lib/sentry/init.js.in index 0069ee017d..e0f8ce7ff7 100644 --- a/developer/src/tike/xml/app/lib/sentry/init.js.in +++ b/developer/src/tike/xml/app/lib/sentry/init.js.in @@ -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() { diff --git a/developer/src/tools/sentry-upload-difs.sh b/developer/src/tools/sentry-upload-difs.sh index 6341952b08..347891698b 100755 --- a/developer/src/tools/sentry-upload-difs.sh +++ b/developer/src/tools/sentry-upload-difs.sh @@ -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 diff --git a/docs/settings/linux/extensions.json b/docs/settings/linux/extensions.json index bd7f528324..55b5ccd393 100644 --- a/docs/settings/linux/extensions.json +++ b/docs/settings/linux/extensions.json @@ -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", ] } diff --git a/docs/settings/linux/launch.json b/docs/settings/linux/launch.json index cbf10cb216..b407dacdb7 100644 --- a/docs/settings/linux/launch.json +++ b/docs/settings/linux/launch.json @@ -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" diff --git a/docs/settings/linux/tasks.json b/docs/settings/linux/tasks.json index f274656555..b2a7f161e5 100644 --- a/docs/settings/linux/tasks.json +++ b/docs/settings/linux/tasks.json @@ -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" diff --git a/docs/websites/README.md b/docs/websites/README.md new file mode 100644 index 0000000000..33c2883500 --- /dev/null +++ b/docs/websites/README.md @@ -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 +``` diff --git a/ios/Cartfile b/ios/Cartfile index fced3730c5..5ad8178ed4 100644 --- a/ios/Cartfile +++ b/ios/Cartfile @@ -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 \ No newline at end of file +github "getsentry/sentry-cocoa" ~> 6.2.1 diff --git a/ios/Cartfile.resolved b/ios/Cartfile.resolved index 6ca0647425..fe74a57322 100644 --- a/ios/Cartfile.resolved +++ b/ios/Cartfile.resolved @@ -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" diff --git a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj index 8cc8d1beea..962fc157aa 100644 --- a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj +++ b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj @@ -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", diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift index 690e413248..475964a6d8 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift @@ -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" diff --git a/ios/engine/KMEI/KeymanEngine/Classes/PackageWebViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/PackageWebViewController.swift index b3b50b9710..f6ec682ac8 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/PackageWebViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/PackageWebViewController.swift @@ -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) { diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/KeyboardSearchViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/KeyboardSearchViewController.swift index 5795a03f38..208e5d3ada 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/KeyboardSearchViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/KeyboardSearchViewController.swift @@ -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 diff --git a/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj b/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj index 2cc124df77..487bfb125a 100644 --- a/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj +++ b/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj @@ -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", diff --git a/ios/kmbuild.sh b/ios/kmbuild.sh index f530b37483..75aff1e1ab 100755 --- a/ios/kmbuild.sh +++ b/ios/kmbuild.sh @@ -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" diff --git a/ios/samples/KMSample2/KMSample2.xcodeproj/project.pbxproj b/ios/samples/KMSample2/KMSample2.xcodeproj/project.pbxproj index defa521f3f..782e5b834a 100644 --- a/ios/samples/KMSample2/KMSample2.xcodeproj/project.pbxproj +++ b/ios/samples/KMSample2/KMSample2.xcodeproj/project.pbxproj @@ -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 = ( diff --git a/ios/tools/KeyboardCalibrator/KeyboardCalibrator.xcodeproj/project.pbxproj b/ios/tools/KeyboardCalibrator/KeyboardCalibrator.xcodeproj/project.pbxproj index 541becc3c1..69426b2230 100644 --- a/ios/tools/KeyboardCalibrator/KeyboardCalibrator.xcodeproj/project.pbxproj +++ b/ios/tools/KeyboardCalibrator/KeyboardCalibrator.xcodeproj/project.pbxproj @@ -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", diff --git a/ios/tools/KeyboardCalibrator/KeyboardCalibrator/DummyInputViewController.swift b/ios/tools/KeyboardCalibrator/KeyboardCalibrator/DummyInputViewController.swift index b66ba6fba6..65be466604 100644 --- a/ios/tools/KeyboardCalibrator/KeyboardCalibrator/DummyInputViewController.swift +++ b/ios/tools/KeyboardCalibrator/KeyboardCalibrator/DummyInputViewController.swift @@ -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 diff --git a/ios/tools/KeyboardCalibrator/KeyboardCalibrator/ViewController.swift b/ios/tools/KeyboardCalibrator/KeyboardCalibrator/ViewController.swift index 551988e5a0..35af76de68 100644 --- a/ios/tools/KeyboardCalibrator/KeyboardCalibrator/ViewController.swift +++ b/ios/tools/KeyboardCalibrator/KeyboardCalibrator/ViewController.swift @@ -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 { diff --git a/linux/debian/control b/linux/debian/control index b42e7bc5c9..0a0446cb1a 100644 --- a/linux/debian/control +++ b/linux/debian/control @@ -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 diff --git a/linux/ibus-keyman/src/engine.c b/linux/ibus-keyman/src/engine.c index 2530ff656a..43b3f01213 100644 --- a/linux/ibus-keyman/src/engine.c +++ b/linux/ibus-keyman/src/engine.c @@ -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, diff --git a/linux/keyman-config/keyman_config/__init__.py b/linux/keyman-config/keyman_config/__init__.py index ee2326a86f..4c57e2d960 100644 --- a/linux/keyman-config/keyman_config/__init__.py +++ b/linux/keyman-config/keyman_config/__init__.py @@ -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]), diff --git a/linux/keyman-config/keyman_config/downloadkeyboard.py b/linux/keyman-config/keyman_config/downloadkeyboard.py index d79b1e3d44..a77008a07b 100755 --- a/linux/keyman-config/keyman_config/downloadkeyboard.py +++ b/linux/keyman-config/keyman_config/downloadkeyboard.py @@ -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): diff --git a/linux/keyman-config/keyman_config/get_kmp.py b/linux/keyman-config/keyman_config/get_kmp.py index 338ff091d8..af7b34063d 100755 --- a/linux/keyman-config/keyman_config/get_kmp.py +++ b/linux/keyman-config/keyman_config/get_kmp.py @@ -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() diff --git a/linux/keyman-config/keyman_config/handle_install.py b/linux/keyman-config/keyman_config/handle_install.py index a6fccc0179..4938f23d41 100644 --- a/linux/keyman-config/keyman_config/handle_install.py +++ b/linux/keyman-config/keyman_config/handle_install.py @@ -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): diff --git a/linux/keyman-config/keyman_config/install_kmp.py b/linux/keyman-config/keyman_config/install_kmp.py index bed23e3f41..19f3d218af 100755 --- a/linux/keyman-config/keyman_config/install_kmp.py +++ b/linux/keyman-config/keyman_config/install_kmp.py @@ -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 diff --git a/linux/keyman-config/keyman_config/install_window.py b/linux/keyman-config/keyman_config/install_window.py index 2220012326..9ae0d41cc0 100755 --- a/linux/keyman-config/keyman_config/install_window.py +++ b/linux/keyman-config/keyman_config/install_window.py @@ -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 diff --git a/linux/keyman-config/keyman_config/keyboard_details.py b/linux/keyman-config/keyman_config/keyboard_details.py index 34f8de4b1d..8f71168c8c 100644 --- a/linux/keyman-config/keyman_config/keyboard_details.py +++ b/linux/keyman-config/keyman_config/keyboard_details.py @@ -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) diff --git a/linux/keyman-config/keyman_config/kmpmetadata.py b/linux/keyman-config/keyman_config/kmpmetadata.py index 83351d5172..4632531e75 100755 --- a/linux/keyman-config/keyman_config/kmpmetadata.py +++ b/linux/keyman-config/keyman_config/kmpmetadata.py @@ -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: diff --git a/linux/keyman-config/keyman_config/uninstall_kmp.py b/linux/keyman-config/keyman_config/uninstall_kmp.py index fe8931dbe3..84c083694d 100755 --- a/linux/keyman-config/keyman_config/uninstall_kmp.py +++ b/linux/keyman-config/keyman_config/uninstall_kmp.py @@ -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 diff --git a/linux/keyman-config/keyman_config/view_installed.py b/linux/keyman-config/keyman_config/view_installed.py index 959c0196d9..671ca2d279 100755 --- a/linux/keyman-config/keyman_config/view_installed.py +++ b/linux/keyman-config/keyman_config/view_installed.py @@ -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: diff --git a/linux/keyman-config/km-config.bash-completion b/linux/keyman-config/km-config.bash-completion index bc28b19212..b1f46c0076 100644 --- a/linux/keyman-config/km-config.bash-completion +++ b/linux/keyman-config/km-config.bash-completion @@ -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 ;; *) diff --git a/linux/keyman-config/km-kvk2ldml.bash-completion b/linux/keyman-config/km-kvk2ldml.bash-completion index c89967666f..3eb2950c39 100644 --- a/linux/keyman-config/km-kvk2ldml.bash-completion +++ b/linux/keyman-config/km-kvk2ldml.bash-completion @@ -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 diff --git a/linux/keyman-config/km-package-get.bash-completion b/linux/keyman-config/km-package-get.bash-completion index 9a1d5ac3aa..9bacb1b8fe 100644 --- a/linux/keyman-config/km-package-get.bash-completion +++ b/linux/keyman-config/km-package-get.bash-completion @@ -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 } diff --git a/linux/keyman-config/km-package-install b/linux/keyman-config/km-package-install index dc2c3a355d..707e144c1f 100755 --- a/linux/keyman-config/km-package-install +++ b/linux/keyman-config/km-package-install @@ -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 ") 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) diff --git a/linux/keyman-config/km-package-install.bash-completion b/linux/keyman-config/km-package-install.bash-completion index 768ea26bb2..1a424e8ea6 100644 --- a/linux/keyman-config/km-package-install.bash-completion +++ b/linux/keyman-config/km-package-install.bash-completion @@ -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 ;; *) diff --git a/linux/keyman-config/km-package-list-installed.bash-completion b/linux/keyman-config/km-package-list-installed.bash-completion index e90bd29699..a3758b467e 100644 --- a/linux/keyman-config/km-package-list-installed.bash-completion +++ b/linux/keyman-config/km-package-list-installed.bash-completion @@ -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 } diff --git a/linux/keyman-config/km-package-uninstall.bash-completion b/linux/keyman-config/km-package-uninstall.bash-completion index 451a2e3e80..191e709eab 100644 --- a/linux/keyman-config/km-package-uninstall.bash-completion +++ b/linux/keyman-config/km-package-uninstall.bash-completion @@ -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 } diff --git a/linux/keyman-config/tests/test_install_kmp.py b/linux/keyman-config/tests/test_install_kmp.py index 804116a42b..2f250550cd 100644 --- a/linux/keyman-config/tests/test_install_kmp.py +++ b/linux/keyman-config/tests/test_install_kmp.py @@ -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() diff --git a/linux/keyman-config/tests/test_package_install_completion.py b/linux/keyman-config/tests/test_package_install_completion.py new file mode 100644 index 0000000000..97238e3dc2 --- /dev/null +++ b/linux/keyman-config/tests/test_package_install_completion.py @@ -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') diff --git a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m index 7b8ac2471c..f40d20b87e 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m +++ b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m @@ -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"; diff --git a/oem/firstvoices/android/app/src/main/java/com/firstvoices/keyboards/MainActivity.java b/oem/firstvoices/android/app/src/main/java/com/firstvoices/keyboards/MainActivity.java index 921f4e1e9a..474c9f4e08 100644 --- a/oem/firstvoices/android/app/src/main/java/com/firstvoices/keyboards/MainActivity.java +++ b/oem/firstvoices/android/app/src/main/java/com/firstvoices/keyboards/MainActivity.java @@ -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); }); diff --git a/oem/firstvoices/ios/Cartfile b/oem/firstvoices/ios/Cartfile index 0f58fa5b5e..ec3127efc7 100644 --- a/oem/firstvoices/ios/Cartfile +++ b/oem/firstvoices/ios/Cartfile @@ -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 diff --git a/oem/firstvoices/ios/Cartfile.resolved b/oem/firstvoices/ios/Cartfile.resolved index 03c8666ea3..16cb6213b6 100644 --- a/oem/firstvoices/ios/Cartfile.resolved +++ b/oem/firstvoices/ios/Cartfile.resolved @@ -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" diff --git a/oem/firstvoices/ios/FirstVoices.xcodeproj/project.pbxproj b/oem/firstvoices/ios/FirstVoices.xcodeproj/project.pbxproj index 3d32a62252..83e34c42a6 100644 --- a/oem/firstvoices/ios/FirstVoices.xcodeproj/project.pbxproj +++ b/oem/firstvoices/ios/FirstVoices.xcodeproj/project.pbxproj @@ -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 = ""; }; CEBD34092654D5AA00EB2EA8 /* Sentry.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Sentry.xcframework; path = Carthage/Build/Sentry.xcframework; sourceTree = ""; }; CEBD340A2654D5AA00EB2EA8 /* XCGLogger.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = XCGLogger.xcframework; path = Carthage/Build/XCGLogger.xcframework; sourceTree = ""; }; - CEBD340B2654D5AA00EB2EA8 /* Zip.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Zip.xcframework; path = Carthage/Build/Zip.xcframework; sourceTree = ""; }; CEBD340C2654D5AB00EB2EA8 /* Reachability.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Reachability.xcframework; path = Carthage/Build/Reachability.xcframework; sourceTree = ""; }; /* 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)", diff --git a/oem/firstvoices/ios/build_common.sh b/oem/firstvoices/ios/build_common.sh index 40a5a01855..a464d2b5a4 100755 --- a/oem/firstvoices/ios/build_common.sh +++ b/oem/firstvoices/ios/build_common.sh @@ -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" diff --git a/oem/firstvoices/windows/src/xml/sentry.init.js.in b/oem/firstvoices/windows/src/xml/sentry.init.js.in index 66bdef75b1..cd32f1d02b 100644 --- a/oem/firstvoices/windows/src/xml/sentry.init.js.in +++ b/oem/firstvoices/windows/src/xml/sentry.init.js.in @@ -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() { diff --git a/resources/build/increment-version.sh b/resources/build/increment-version.sh index 6559bce793..3c1eaa56fa 100755 --- a/resources/build/increment-version.sh +++ b/resources/build/increment-version.sh @@ -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 diff --git a/web/source/build.sh b/web/source/build.sh index 8cfe224f00..fd1965e132 100755 --- a/web/source/build.sh +++ b/web/source/build.sh @@ -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 diff --git a/windows/src/buildtools/sentry-upload-difs.sh b/windows/src/buildtools/sentry-upload-difs.sh index f6a67dafed..6eaaf49f4a 100755 --- a/windows/src/buildtools/sentry-upload-difs.sh +++ b/windows/src/buildtools/sentry-upload-difs.sh @@ -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 diff --git a/windows/src/desktop/help/about/welcome.md b/windows/src/desktop/help/about/welcome.md index 475b391d52..79c01e4fcd 100644 --- a/windows/src/desktop/help/about/welcome.md +++ b/windows/src/desktop/help/about/welcome.md @@ -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 diff --git a/windows/src/desktop/help/basic/config/keyboards.md b/windows/src/desktop/help/basic/config/keyboards.md index fdc4c5a638..21130659ee 100644 --- a/windows/src/desktop/help/basic/config/keyboards.md +++ b/windows/src/desktop/help/basic/config/keyboards.md @@ -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 Ctrl + Alt 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 ( + Ctrl + C, Ctrl + V, 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 diff --git a/windows/src/desktop/help/basic/enable-or-disable-keyboard.md b/windows/src/desktop/help/basic/enable-or-disable-keyboard.md index 0d9fb6733c..3355fc6f5f 100644 --- a/windows/src/desktop/help/basic/enable-or-disable-keyboard.md +++ b/windows/src/desktop/help/basic/enable-or-disable-keyboard.md @@ -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 diff --git a/windows/src/desktop/help/basic/uninstall-keyboard.md b/windows/src/desktop/help/basic/uninstall-keyboard.md index f98c6daaac..b36ff14240 100644 --- a/windows/src/desktop/help/basic/uninstall-keyboard.md +++ b/windows/src/desktop/help/basic/uninstall-keyboard.md @@ -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. diff --git a/windows/src/desktop/help/desktop_images/download-keyboard-step0.png b/windows/src/desktop/help/desktop_images/download-keyboard-step0.png index cd48fe0ab6..ad1c7f3a5a 100644 Binary files a/windows/src/desktop/help/desktop_images/download-keyboard-step0.png and b/windows/src/desktop/help/desktop_images/download-keyboard-step0.png differ diff --git a/windows/src/desktop/help/desktop_images/download-keyboard-step5.png b/windows/src/desktop/help/desktop_images/download-keyboard-step5.png index 978f79c09e..4f621c0738 100644 Binary files a/windows/src/desktop/help/desktop_images/download-keyboard-step5.png and b/windows/src/desktop/help/desktop_images/download-keyboard-step5.png differ diff --git a/windows/src/desktop/help/desktop_images/keyboard-share.png b/windows/src/desktop/help/desktop_images/keyboard-share.png index b009edbb10..73d0fa720c 100644 Binary files a/windows/src/desktop/help/desktop_images/keyboard-share.png and b/windows/src/desktop/help/desktop_images/keyboard-share.png differ diff --git a/windows/src/desktop/help/desktop_images/keyboards-hotkey.png b/windows/src/desktop/help/desktop_images/keyboards-hotkey.png deleted file mode 100644 index 35501f353e..0000000000 Binary files a/windows/src/desktop/help/desktop_images/keyboards-hotkey.png and /dev/null differ diff --git a/windows/src/desktop/help/desktop_images/language-association.png b/windows/src/desktop/help/desktop_images/language-association.png index 112f87f6aa..101ca42826 100644 Binary files a/windows/src/desktop/help/desktop_images/language-association.png and b/windows/src/desktop/help/desktop_images/language-association.png differ diff --git a/windows/src/desktop/help/desktop_images/options-advanced.png b/windows/src/desktop/help/desktop_images/options-advanced.png index f208cd9581..8e80f9e000 100644 Binary files a/windows/src/desktop/help/desktop_images/options-advanced.png and b/windows/src/desktop/help/desktop_images/options-advanced.png differ diff --git a/windows/src/desktop/help/desktop_images/options-general.png b/windows/src/desktop/help/desktop_images/options-general.png index cb17931e68..65577cc92f 100644 Binary files a/windows/src/desktop/help/desktop_images/options-general.png and b/windows/src/desktop/help/desktop_images/options-general.png differ diff --git a/windows/src/desktop/help/desktop_images/tab-add-lang.png b/windows/src/desktop/help/desktop_images/tab-add-lang.png new file mode 100644 index 0000000000..1c71a8e98c Binary files /dev/null and b/windows/src/desktop/help/desktop_images/tab-add-lang.png differ diff --git a/windows/src/desktop/help/desktop_images/tab-add-popup.png b/windows/src/desktop/help/desktop_images/tab-add-popup.png new file mode 100644 index 0000000000..6fd132d364 Binary files /dev/null and b/windows/src/desktop/help/desktop_images/tab-add-popup.png differ diff --git a/windows/src/desktop/help/desktop_images/tab-hotkeys.png b/windows/src/desktop/help/desktop_images/tab-hotkeys.png index a9354f645c..37df3c0c12 100644 Binary files a/windows/src/desktop/help/desktop_images/tab-hotkeys.png and b/windows/src/desktop/help/desktop_images/tab-hotkeys.png differ diff --git a/windows/src/desktop/help/desktop_images/tab-keyboards.png b/windows/src/desktop/help/desktop_images/tab-keyboards.png index d909592678..c210430b42 100644 Binary files a/windows/src/desktop/help/desktop_images/tab-keyboards.png and b/windows/src/desktop/help/desktop_images/tab-keyboards.png differ diff --git a/windows/src/desktop/help/desktop_images/tab-layout-detail1.png b/windows/src/desktop/help/desktop_images/tab-layout-detail1.png new file mode 100644 index 0000000000..1d7af670c3 Binary files /dev/null and b/windows/src/desktop/help/desktop_images/tab-layout-detail1.png differ diff --git a/windows/src/desktop/help/desktop_images/tab-layout-detail2.png b/windows/src/desktop/help/desktop_images/tab-layout-detail2.png new file mode 100644 index 0000000000..6fc3b84282 Binary files /dev/null and b/windows/src/desktop/help/desktop_images/tab-layout-detail2.png differ diff --git a/windows/src/desktop/help/desktop_images/tab-layout-disable.png b/windows/src/desktop/help/desktop_images/tab-layout-disable.png new file mode 100644 index 0000000000..24f1dc9193 Binary files /dev/null and b/windows/src/desktop/help/desktop_images/tab-layout-disable.png differ diff --git a/windows/src/desktop/help/desktop_images/tab-layout-enable.png b/windows/src/desktop/help/desktop_images/tab-layout-enable.png new file mode 100644 index 0000000000..4a8e9e6097 Binary files /dev/null and b/windows/src/desktop/help/desktop_images/tab-layout-enable.png differ diff --git a/windows/src/desktop/help/desktop_images/tab-layout-help.png b/windows/src/desktop/help/desktop_images/tab-layout-help.png new file mode 100644 index 0000000000..73187d498f Binary files /dev/null and b/windows/src/desktop/help/desktop_images/tab-layout-help.png differ diff --git a/windows/src/desktop/help/desktop_images/tab-layout-share.png b/windows/src/desktop/help/desktop_images/tab-layout-share.png new file mode 100644 index 0000000000..cf09c019ee Binary files /dev/null and b/windows/src/desktop/help/desktop_images/tab-layout-share.png differ diff --git a/windows/src/desktop/help/desktop_images/tab-layout.png b/windows/src/desktop/help/desktop_images/tab-layout.png index 9dab8bf592..3793101314 100644 Binary files a/windows/src/desktop/help/desktop_images/tab-layout.png and b/windows/src/desktop/help/desktop_images/tab-layout.png differ diff --git a/windows/src/desktop/help/desktop_images/tab-options2.png b/windows/src/desktop/help/desktop_images/tab-options2.png index 16b4e5816a..54ca19eadb 100644 Binary files a/windows/src/desktop/help/desktop_images/tab-options2.png and b/windows/src/desktop/help/desktop_images/tab-options2.png differ diff --git a/windows/src/desktop/help/desktop_images/tab-support.png b/windows/src/desktop/help/desktop_images/tab-support.png index b77aa34174..d4823eabc0 100644 Binary files a/windows/src/desktop/help/desktop_images/tab-support.png and b/windows/src/desktop/help/desktop_images/tab-support.png differ diff --git a/windows/src/desktop/help/start/configure-computer.md b/windows/src/desktop/help/start/configure-computer.md index 65728969e4..014fb54a5b 100644 --- a/windows/src/desktop/help/start/configure-computer.md +++ b/windows/src/desktop/help/start/configure-computer.md @@ -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 diff --git a/windows/src/desktop/kmshell/install/UfrmInstallKeyboardLanguage.pas b/windows/src/desktop/kmshell/install/UfrmInstallKeyboardLanguage.pas index 78524e79de..fab24635f7 100644 --- a/windows/src/desktop/kmshell/install/UfrmInstallKeyboardLanguage.pas +++ b/windows/src/desktop/kmshell/install/UfrmInstallKeyboardLanguage.pas @@ -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; diff --git a/windows/src/desktop/kmshell/xml/sentry.init.js.in b/windows/src/desktop/kmshell/xml/sentry.init.js.in index 66bdef75b1..cd32f1d02b 100644 --- a/windows/src/desktop/kmshell/xml/sentry.init.js.in +++ b/windows/src/desktop/kmshell/xml/sentry.init.js.in @@ -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() {