mirror of
https://github.com/keymanapp/keyman.git
synced 2026-10-18 20:07:39 +00:00
Merge pull request #13510 from keymanapp/chore/merge-master-into-web-core
chore: merge master into web-core 🎼
This commit is contained in:
commit
a9e002e5ea
341 changed files with 7727 additions and 2898 deletions
3
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
3
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -18,6 +18,9 @@ body:
|
|||
You may also wish to discuss your bug report with the Keyman community at:
|
||||
https://community.software.sil.org/c/keyman
|
||||
|
||||
Are you using Linux and fcitx5? If so, please create issues at:
|
||||
https://github.com/fcitx/fcitx5-keyman/issues/new/choose
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
|
|
|
|||
177
HISTORY.md
177
HISTORY.md
|
|
@ -1,5 +1,22 @@
|
|||
# Keyman Version History
|
||||
|
||||
## 19.0.13 alpha 2025-03-12
|
||||
|
||||
* feat(windows): hard code key cap colour (#13444)
|
||||
|
||||
## 19.0.12 alpha 2025-03-04
|
||||
|
||||
* chore: remove legacy IIS setup notes (#13403)
|
||||
|
||||
## 19.0.11 alpha 2025-03-03
|
||||
|
||||
* chore(linux): Update debian changelog (#13347)
|
||||
* fix(common): improve resilience of build script trigger (#13402)
|
||||
|
||||
## 19.0.10 alpha 2025-03-01
|
||||
|
||||
* chore: merge beta into master B18S2 (#13391)
|
||||
|
||||
## 19.0.9 alpha 2025-02-27
|
||||
|
||||
* docs: move node.js before Emscripten (#13366)
|
||||
|
|
@ -42,6 +59,166 @@
|
|||
* refactor(windows): rename `TKeymanMutex.MutexOwned` to `TakeOwnership` and add `ReleaseOwnership` (#13168)
|
||||
* chore: increment to alpha 19.0 (#13187)
|
||||
|
||||
## 18.0.214 beta 2025-03-13
|
||||
|
||||
* fix(mac): change text in privacy alert for macOS 13.0 and later (#13480)
|
||||
* docs(mac): update troubleshooting page for macOS security (#13476)
|
||||
* fix(core): unalign `usetCount` and `rangeCount` (#13450)
|
||||
* fix(developer): strip markers when generating KVK from LDML (#13479)
|
||||
|
||||
## 18.0.213 beta 2025-03-12
|
||||
|
||||
* docs(developer): fix typos in readme and add link (#13414)
|
||||
* fix(developer): add folder text to Error_FileDoesNotExist message (#13457)
|
||||
* fix(developer): load Keyman Developer options in Server before use (#13459)
|
||||
* chore(core): add `clean` actions for `:mac` and `:win` targets (#13460)
|
||||
* chore(common): update langtags.json to 2025-02-18 release (#13461)
|
||||
* fix(developer): use '-' as placeholder for temp project name in cmdline (#13462)
|
||||
* chore: handle failure to retrieve GitHub PR status in build triggers (#13474)
|
||||
|
||||
## 18.0.212 beta 2025-03-11
|
||||
|
||||
* fix(oem/fv/android): Avoid resetting keyboard index (#13449)
|
||||
* fix(android/engine): Ignore key errors if user switched to another IME (#13466)
|
||||
* fix(android): Cache inputType to determine external keyboard handling (#13475)
|
||||
|
||||
## 18.0.211 beta 2025-03-10
|
||||
|
||||
* feat(windows): hide update header if no updates are available (#13442)
|
||||
* fix(ios): suppress Get Started dialog when closing help page (#13422)
|
||||
* docs(ios): update minimum supported ios version (#13452)
|
||||
|
||||
## 18.0.210 beta 2025-03-07
|
||||
|
||||
* chore(common): Add crowdin localization for Vietnamese (#13322)
|
||||
* chore(linux): add link to fcitx5 issues to bug issue template (#13451)
|
||||
|
||||
## 18.0.209 beta 2025-03-06
|
||||
|
||||
* fix(common): fail builds with low test coverage for all typescript projects (#13418)
|
||||
* fix(windows): Check for updates registry value stored under the one Key (#13413)
|
||||
* fix(windows): remove online update deadcode (#13421)
|
||||
|
||||
## 18.0.208 beta 2025-03-05
|
||||
|
||||
* fix(windows): Check registry report usage value in setup process (#13215)
|
||||
* fix(android): Don't report missing kmp.json (#13411)
|
||||
|
||||
## 18.0.207 beta 2025-03-04
|
||||
|
||||
* fix(common): improve resilience of build script trigger (#13407)
|
||||
* fix(mac): improve compliance checking (#13337)
|
||||
* chore(common): add unit test for nul and context() in context part of rule (#13412)
|
||||
|
||||
## 18.0.206 beta 2025-03-03
|
||||
|
||||
* fix(android): Control when system keyboard Keyboard Picker clears activity stack (#13313)
|
||||
* docs(android): document control flag for system keyboard picker and task (#13373)
|
||||
|
||||
## 18.0.205 beta 2025-03-02
|
||||
|
||||
* chore(linux): Update debian changelog (#13346)
|
||||
|
||||
## 18.0.204 beta 2025-03-01
|
||||
|
||||
* chore(windows): ensure dependencies for keyman32 exclude core:wasm (#13379)
|
||||
|
||||
## 18.0.203 beta 2025-02-28
|
||||
|
||||
* feat(windows): keyman configuration icon refresh (#13294)
|
||||
* fix(windows): if kmshell is "silent" don't start a Keyman update (#13330)
|
||||
* chore(windows): Update crowdin strings for Khmer (#13364)
|
||||
* docs: clean up links in builder.md (#13328)
|
||||
* chore: add build.sh edit action for Delphi projects (#13327)
|
||||
* fix(developer): handle line breaks in debugger correctly (#13334)
|
||||
* fix(developer): use U+0001 for deadkey markers in the debugger (#13336)
|
||||
* feat(developer): use .kps folder as initial dir for Add Files (#13324)
|
||||
* fix(developer): verify `context(n)` offsets are valid in kmcmplib (#13308)
|
||||
* chore(core): add unit tests to validate `nul` with `index` and `context` (#13311)
|
||||
* fix(core): include `nul` in context offset calculations in kmx processor (#13314)
|
||||
* fix(developer): include `nul` in offset calculations for kmw v10 compiler (#13326)
|
||||
|
||||
## 18.0.202 beta 2025-02-27
|
||||
|
||||
* fix(android): Direct user to switch system IME when WebView fails (#13352)
|
||||
* fix(android): Filter out SocketTimeoutExceptions (#13353)
|
||||
* refactor(linux): extract method for setting up mocks in tests (#13355)
|
||||
* chore(linux): update to current Debian policy version (#13362)
|
||||
* fix(developer): prevent double action of editor hotkeys in debugger (#13345)
|
||||
* fix(linux): ignore DBusException during install and uninstall (#13356)
|
||||
* fix(linux): ignore exception when fcitx5-configtool is not found (#13363)
|
||||
* fix(developer): prevent simultaneous opening of the same project by two processes (#13341)
|
||||
* chore: add a workflow to automatically close linked issues (#13369)
|
||||
|
||||
## 18.0.201 beta 2025-02-26
|
||||
|
||||
* fix(web): prevent cookie-default behavior from mangling engine init (#13331)
|
||||
* chore(linux): adjust to renamed xdg module (#13354)
|
||||
|
||||
## 18.0.200 beta 2025-02-25
|
||||
|
||||
* fix(web): properly support workerless use of Web engine (#13290)
|
||||
* fix(web): reconnect, patch up engine/main headless unit tests (#13291)
|
||||
* change(ios): re-order host page script load so Sentry can report script load failures (#13332)
|
||||
* chore(linux): Update to new Debian policy version (#13325)
|
||||
|
||||
## 18.0.199 beta 2025-02-24
|
||||
|
||||
* fix(windows): Update Greek Polytonic for keyboard help (#13323)
|
||||
|
||||
## 18.0.198 beta 2025-02-21
|
||||
|
||||
* fix(android): Update system keyboard height consistently (#13273)
|
||||
|
||||
## 18.0.197 beta 2025-02-20
|
||||
|
||||
* fix(linux): address compiling with ICU 76 (#13285)
|
||||
|
||||
## 18.0.196 beta 2025-02-19
|
||||
|
||||
* fix(windows): remove sentry report on firstrun in idle state (#13266)
|
||||
* fix(android): Correct the scope of `tier` in build.gradle (#13268)
|
||||
* chore(common): Update crowdin strings for Polytonic Greek (#13277)
|
||||
* fix(web): provide informative error + message when loading UI without the engine (#13269)
|
||||
* fix(developer): Use 'desktop' for target in OSK fill temp keyboard (#13271)
|
||||
* fix(linux): add missing dependency for uploading to llso (#13279)
|
||||
* refactor(linux): re-enable integration tests (#13234)
|
||||
|
||||
## 18.0.195 beta 2025-02-18
|
||||
|
||||
* docs(web): use promise-chaining in manual-control example (#13272)
|
||||
|
||||
## 18.0.194 beta 2025-02-17
|
||||
|
||||
* fix(android): Use TIER.md to determine Play Store tier (#13259)
|
||||
* fix(android): prevent update-checks from crashing system keyboard and app if `DownloadManager` is disabled (#13218)
|
||||
* feat(linux): add `--remote-debug` flag to `run-tests.sh` script (#13233)
|
||||
* chore(linux): improve output when running tests (#13247)
|
||||
* refactor(linux): cleanup code of test fixture (#13248)
|
||||
|
||||
## 18.0.193 beta 2025-02-14
|
||||
|
||||
* chore(developer): add INFO messages for `kmc copy` and `kmc generate` (#13200)
|
||||
* fix(android): Remove sendOptionsToKeyboard function from Engine API (#13219)
|
||||
* docs(android): Document removing sendOptionsToKeyboard API (#13220)
|
||||
* chore(ios): increase minimum ios version to 15 (#13201)
|
||||
* fix(android): Catch WebView exceptions and prompt user to install WebView (#13140)
|
||||
* chore(common): Update crowdin strings for Mon (#13241)
|
||||
* chore(ios,windows): Update Crowdin strings for Czech (#13242)
|
||||
* change(web): drop 'dom' unit test concurrency when run in CI mode (#13141)
|
||||
* chore(web): ensure /web/build.sh clean triggers all predictive-text child actions (#13158)
|
||||
* chore(web): fix coverage-reporting artifact (#13196)
|
||||
|
||||
## 18.0.192 beta 2025-02-14
|
||||
|
||||
* chore(common): Fix 17.0.335 tier in HISTORY.md (#13213)
|
||||
* docs(linux): update what's new for 18.0 (#13207)
|
||||
* chore(linux): additional code cleanup after Focal removal (#13206)
|
||||
* docs(windows): update keyman for windows 18.0 whats-new (#13211)
|
||||
* chore: undo master merge (#13238)
|
||||
* change(ios): prevent memory leaks from keyboard-related notification observers (#13129)
|
||||
* change(ios): mitigate keyboard input-view memory leak by force-discarding WebView (#13169)
|
||||
|
||||
## 18.0.191 beta 2025-02-12
|
||||
|
||||
* feat(windows): handle a hard windows reset occurring while downloading updated keyman files (#13128)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
19.0.10
|
||||
19.0.14
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ plugins {
|
|||
ext.rootPath = '../../'
|
||||
apply from: "$rootPath/version.gradle"
|
||||
|
||||
String tier = new File('../../TIER.md').getText('UTF-8').trim();
|
||||
|
||||
android {
|
||||
compileSdk 34
|
||||
namespace="com.tavultesoft.kmapro"
|
||||
|
|
@ -75,7 +77,7 @@ android {
|
|||
// - PR builds also have "-test-1234" suffix (where 1234 is PR#)
|
||||
// Gradle sync will nullify these values, so have a fallback
|
||||
String appSuffix = ''
|
||||
String tag = VERSION_TAG ? VERSION_TAG : '-' + new File('../../TIER.md').getText('UTF-8');
|
||||
String tag = VERSION_TAG ? VERSION_TAG : '-' + tier;
|
||||
if (tag.startsWithAny("-beta", "beta")) {
|
||||
appSuffix = ' Beta';
|
||||
} else if (tag.startsWithAny("-alpha", "alpha")) {
|
||||
|
|
@ -127,7 +129,7 @@ if (env_keys_json_file != null && file(env_keys_json_file).exists()) {
|
|||
|
||||
// Deactivate lower conflicting version APKs
|
||||
resolutionStrategy.set(com.github.triplet.gradle.androidpublisher.ResolutionStrategy.IGNORE)
|
||||
switch (System.env.TIER) {
|
||||
switch (tier) {
|
||||
case 'beta':
|
||||
track.set("beta")
|
||||
println "TIER set to beta"
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
package com.keyman.android;
|
||||
|
||||
import com.keyman.engine.util.DownloadFileUtils;
|
||||
import com.tavultesoft.kmapro.AdjustLongpressDelayActivity;
|
||||
import com.tavultesoft.kmapro.BuildConfig;
|
||||
import com.tavultesoft.kmapro.DefaultLanguageResource;
|
||||
|
|
@ -44,6 +45,7 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
|
|||
private static View inputView = null;
|
||||
private static ExtractedText exText = null;
|
||||
private KMHardwareKeyboardInterpreter interpreter = null;
|
||||
private int inputType = InputType.TYPE_NULL;
|
||||
private int lastOrientation = Configuration.ORIENTATION_UNDEFINED;
|
||||
|
||||
private static final String TAG = "SystemKeyboard";
|
||||
|
|
@ -84,7 +86,12 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
|
|||
boolean mayHaveHapticFeedback = prefs.getBoolean(KeymanSettingsActivity.hapticFeedbackKey, false);
|
||||
KMManager.setHapticFeedback(mayHaveHapticFeedback);
|
||||
|
||||
KMManager.executeResourceUpdate(this);
|
||||
// Checking for updates should never be allowed to crash the keyboard.
|
||||
// Just silently fail if this occurs.
|
||||
if(DownloadFileUtils.getDownloadManager(this) != null) {
|
||||
// Will try to emit a toast if it fails - i.e., is not silent.
|
||||
KMManager.executeResourceUpdate(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -149,18 +156,9 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
|
|||
KMManager.onStartInput(attribute, restarting);
|
||||
KMManager.resetContext(KeyboardType.KEYBOARD_TYPE_SYSTEM);
|
||||
|
||||
// This method (likely) includes the IME equivalent to `onResume` for `Activity`-based classes,
|
||||
// making it an important time to detect orientation changes.
|
||||
Context appContext = getApplicationContext();
|
||||
int newOrientation = KMManager.getOrientation(appContext);
|
||||
if(newOrientation != lastOrientation) {
|
||||
lastOrientation = newOrientation;
|
||||
Configuration newConfig = this.getResources().getConfiguration();
|
||||
KMManager.onConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
// Temporarily disable predictions on certain fields (e.g. hidden password field or numeric)
|
||||
int inputType = attribute.inputType;
|
||||
inputType = attribute.inputType;
|
||||
KMManager.setMayPredictOverride(inputType);
|
||||
if (KMManager.getMayPredictOverride()) {
|
||||
KMManager.setBannerOptions(false);
|
||||
|
|
@ -207,6 +205,9 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
|
|||
public void onStartInputView(EditorInfo attribute, boolean restarting) {
|
||||
super.onStartInputView(attribute, restarting);
|
||||
setInputView(onCreateInputView());
|
||||
|
||||
// Update the input type
|
||||
inputType = attribute.inputType;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -228,6 +229,16 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
|
|||
public void onComputeInsets(InputMethodService.Insets outInsets) {
|
||||
super.onComputeInsets(outInsets);
|
||||
|
||||
// This method (likely) includes the IME equivalent to `onResume` for `Activity`-based classes,
|
||||
// making it an important time to detect orientation changes.
|
||||
Context appContext = getApplicationContext();
|
||||
int newOrientation = KMManager.getOrientation(appContext);
|
||||
if(newOrientation != lastOrientation) {
|
||||
lastOrientation = newOrientation;
|
||||
Configuration newConfig = this.getResources().getConfiguration();
|
||||
KMManager.onConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
// We should extend the touchable region so that Keyman sub keys menu can receive touch events outside the keyboard frame
|
||||
Point size = KMManager.getWindowSize(getApplicationContext());
|
||||
|
||||
|
|
@ -250,8 +261,6 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
|
|||
if (exText != null)
|
||||
exText = null;
|
||||
}
|
||||
// Initialize keyboard options
|
||||
KMManager.sendOptionsToKeyboard();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -271,6 +280,11 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
|
|||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
// Determine if Physical keystroke should be passed off
|
||||
if (inputType == InputType.TYPE_NULL) {
|
||||
return false; // Revert to default handling
|
||||
}
|
||||
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
|
|
|
|||
|
|
@ -133,7 +133,6 @@ public class AdjustLongpressDelayActivity extends BaseActivity {
|
|||
// Store the longpress delay as a reference
|
||||
// and then update KeymanWeb with the longpress delay
|
||||
KMManager.setLongpressDelay(currentDelayTimeMS);
|
||||
KMManager.sendOptionsToKeyboard();
|
||||
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import android.graphics.Bitmap;
|
|||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.util.AndroidRuntimeException;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
|
@ -70,7 +71,13 @@ public class KMPBrowserActivity extends BaseActivity {
|
|||
}
|
||||
}
|
||||
|
||||
setContentView(R.layout.activity_kmp_browser);
|
||||
try {
|
||||
setContentView(R.layout.activity_kmp_browser);
|
||||
} catch (AndroidRuntimeException e) {
|
||||
// WebView not enabled
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
webView = (WebView) findViewById(R.id.kmpBrowserWebView);
|
||||
webView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tavultesoft.kmapro;
|
|||
import android.os.Bundle;
|
||||
|
||||
import com.keyman.engine.BaseActivity;
|
||||
import com.keyman.engine.KMManager;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
|
@ -32,6 +33,9 @@ public class KeymanSettingsActivity extends BaseActivity {
|
|||
}
|
||||
|
||||
innerFragment = (KeymanSettingsFragment) getSupportFragmentManager().findFragmentById(R.id.keyman_settings_fragment);
|
||||
|
||||
// For Keyman sites, disable keyboard picker task flag so keyboard picker doesn't dismiss Keyman app
|
||||
KMManager.dontCloseParentAppOnShowKeyboardPicker();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -43,7 +43,9 @@ import com.keyman.android.DownloadIntentService;
|
|||
import com.keyman.engine.util.KMLog;
|
||||
import com.keyman.engine.util.KMPLink;
|
||||
import com.keyman.engine.util.KMString;
|
||||
import com.keyman.engine.util.WebViewUtils;
|
||||
import com.keyman.engine.util.WebViewUtils.EngineWebViewVersionStatus;
|
||||
import com.keyman.engine.util.WebViewUtils.SystemWebViewStatus;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.ProgressDialog;
|
||||
|
|
@ -153,7 +155,9 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
|
|||
KMManager.setDebugMode(true);
|
||||
}
|
||||
|
||||
// Verify WebView installed and enabled before attempting to initialize KMManager
|
||||
KMManager.initialize(getApplicationContext(), KeyboardType.KEYBOARD_TYPE_INAPP);
|
||||
|
||||
KMManager.executeResourceUpdate(this);
|
||||
|
||||
DefaultLanguageResource.install(context);
|
||||
|
|
@ -181,8 +185,11 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
|
|||
textView.setTextSize((float) textSize);
|
||||
textView.setSelection(textView.getText().length());
|
||||
|
||||
// Use in-app keyboard WebView to check the Chrome version
|
||||
checkChromeVersion(KMManager.getKMKeyboard(KeyboardType.KEYBOARD_TYPE_INAPP));
|
||||
// Check WebView enabled and Chrome version
|
||||
SystemWebViewStatus webViewStatus = WebViewUtils.getSystemWebViewStatus(context);
|
||||
EngineWebViewVersionStatus chromeStatus = KMManager.getEngineWebViewVersionStatus();
|
||||
updateWebViewIfNeeded(chromeStatus, webViewStatus);
|
||||
|
||||
CheckInstallReferrer.checkGooglePlayInstallReferrer(this, context);
|
||||
checkGetStarted();
|
||||
}
|
||||
|
|
@ -240,6 +247,9 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
|
|||
KMManager.onResume();
|
||||
KMManager.hideSystemKeyboard();
|
||||
|
||||
// Reset keyboard picker Activity Task flag
|
||||
KMManager.closeParentAppOnShowKeyboardPicker();
|
||||
|
||||
// onConfigurationChanged() only triggers when device is rotated while app is in foreground
|
||||
// This handles when device is rotated while app is in background
|
||||
// using KMManager.getOrientation() since getConfiguration().orientation is unreliable #10241
|
||||
|
|
@ -475,8 +485,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
|
|||
|
||||
@Override
|
||||
public void onKeyboardLoaded(KeyboardType keyboardType) {
|
||||
// Initialize keyboard options
|
||||
KMManager.sendOptionsToKeyboard();
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -770,39 +779,114 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
|
|||
KMManager.setHapticFeedback(mayHaveHapticFeedback);
|
||||
}
|
||||
|
||||
private void checkChromeVersion(WebView webView) {
|
||||
if (KMManager.getEngineWebViewVersionStatus() != EngineWebViewVersionStatus.FULL) {
|
||||
LinearLayout updateChromeLayout = (LinearLayout) findViewById(R.id.updateChromeLayout);
|
||||
updateChromeLayout.setVisibility(View.VISIBLE);
|
||||
|
||||
Button updateChromeButton = (Button)findViewById(R.id.updateChromeButton);
|
||||
updateChromeButton.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
// Launch PlayStore to update Chrome
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.android.chrome"));
|
||||
if (intent.resolveActivity(getPackageManager()) != null) {
|
||||
startActivity(intent);
|
||||
} else {
|
||||
intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=com.android.chrome"));
|
||||
if (intent.resolveActivity(getPackageManager()) != null) {
|
||||
startActivity(intent);
|
||||
} else {
|
||||
Toast.makeText(getApplicationContext(), getString(R.string.unable_to_open_browser), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
// Link to Chrome if user is not signed in to Play Store
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=com.android.chrome"));
|
||||
if (intent.resolveActivity(getPackageManager()) != null) {
|
||||
startActivity(intent);
|
||||
} else {
|
||||
Toast.makeText(getApplicationContext(), getString(R.string.unable_to_open_browser), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
/**
|
||||
* Based on whether Chrome and WebView are installed & enabled,
|
||||
* display the corresponding update panel
|
||||
* @param chromeStatus
|
||||
* @param webViewStatus
|
||||
*/
|
||||
private void updateWebViewIfNeeded(EngineWebViewVersionStatus chromeStatus, SystemWebViewStatus webViewStatus) {
|
||||
if (chromeStatus == EngineWebViewVersionStatus.FULL && webViewStatus == SystemWebViewStatus.FULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (webViewStatus == SystemWebViewStatus.NOT_INSTALLED) {
|
||||
displayInstallWebView();
|
||||
} else if (webViewStatus == SystemWebViewStatus.DISABLED) {
|
||||
displayEnableWebView();
|
||||
} else {
|
||||
// Otherwise, display update Chrome
|
||||
displayUpdateChrome();
|
||||
}
|
||||
}
|
||||
|
||||
private void displayInstallWebView() {
|
||||
TextView textView = (TextView)findViewById((R.id.kmWebViewChromeTextView));
|
||||
textView.setText(getString(R.string.body_install_webview));
|
||||
|
||||
Button button = (Button)findViewById(R.id.webViewChromeButton);
|
||||
button.setText(getString(R.string.label_install_webview));
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
// Play Store link to install WebView
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW,
|
||||
Uri.parse("https://play.google.com/store/apps/details?id=com.google.android.webview"));
|
||||
|
||||
// Launch intent
|
||||
try {
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
if (intent.resolveActivity(packageManager) != null) {
|
||||
startActivity(intent);
|
||||
}
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
Toast.makeText(getApplicationContext(), getString(R.string.unable_to_open_browser), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
LinearLayout webViewChromeLayout = (LinearLayout) findViewById(R.id.checkWebViewChromeLayout);
|
||||
webViewChromeLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void displayEnableWebView() {
|
||||
TextView textView = (TextView)findViewById((R.id.kmWebViewChromeTextView));
|
||||
textView.setText(getString(R.string.body_enable_webview));
|
||||
|
||||
Button button = (Button)findViewById(R.id.webViewChromeButton);
|
||||
button.setText(getString(R.string.label_enable_webview));
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
// Application settings to enable WebView
|
||||
Intent intent = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS",
|
||||
Uri.parse("package:com.google.android.webview"));
|
||||
|
||||
// Launch intent
|
||||
try {
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
if (intent.resolveActivity(packageManager) != null) {
|
||||
startActivity(intent);
|
||||
}
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
Toast.makeText(getApplicationContext(), getString(R.string.unable_to_open_browser), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
LinearLayout webViewChromeLayout = (LinearLayout) findViewById(R.id.checkWebViewChromeLayout);
|
||||
webViewChromeLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void displayUpdateChrome() {
|
||||
// TextView's default string is to update Chrome
|
||||
|
||||
Button button = (Button)findViewById(R.id.webViewChromeButton);
|
||||
button.setText(getString(R.string.button_update_chrome));
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
// Primary Play Store link to install/update Chrome
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW,
|
||||
Uri.parse("market://details?id=com.android.chrome"));
|
||||
|
||||
try {
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
if (intent.resolveActivity(packageManager) != null) {
|
||||
startActivity(intent);
|
||||
} else {
|
||||
// (alternate Play Store link to install/update Chrome)
|
||||
intent = new Intent(Intent.ACTION_VIEW,
|
||||
Uri.parse("https://play.google.com/store/apps/details?id=com.android.chrome"));
|
||||
|
||||
intent.resolveActivity(packageManager);
|
||||
startActivity(intent);
|
||||
}
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
Toast.makeText(getApplicationContext(), getString(R.string.unable_to_open_browser), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
LinearLayout webViewChromeLayout = (LinearLayout) findViewById(R.id.checkWebViewChromeLayout);
|
||||
webViewChromeLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private Uri requestPermissionIntentUri;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
<include layout="@layout/titlebar" />
|
||||
|
||||
<include layout="@layout/update_chrome_layout"
|
||||
<include layout="@layout/check_chrome_webview_layout"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
android:background="@drawable/textview_bg"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/updateChromeLayout"
|
||||
android:layout_below="@id/checkWebViewChromeLayout"
|
||||
android:ems="10"
|
||||
android:inputType="textMultiLine|textNoSuggestions"
|
||||
android:hint="@string/textview_hint"
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/updateChromeLayout"
|
||||
android:id="@+id/checkWebViewChromeLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/white"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<!-- Banner to display if old version of Chrome installed -->
|
||||
<!-- Banner to display if WebView is not installed/enabled or old version of Chrome installed -->
|
||||
<TextView
|
||||
android:id="@+id/kmUpgradeChromeTextView"
|
||||
android:id="@+id/kmWebViewChromeTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
android:textSize="@dimen/update_chrome_label_textsize" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/updateChromeButton"
|
||||
android:id="@+id/webViewChromeButton"
|
||||
style="@style/Widget.AppCompat.Button.Colored"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
<!-- Context: Menu Action -->
|
||||
<string name="action_settings" comment="Menu action to open Settings menu">Ρυθμίσεις</string>
|
||||
<!-- Context: Menu Action -->
|
||||
<string name="action_install_updates" comment="Menu notification that keyboard or dictionary updates available">Εγκατάσταση Ενημερώσεων</string>
|
||||
<string name="action_install_updates" comment="Menu notification that keyboard or dictionary updates available">Ἐγκατάσταση Ἐνημερώσεων</string>
|
||||
<!-- Context: Title -->
|
||||
<string name="title_version" comment="Title of Keyman for Android version">Ἔκδοση %1$s</string>
|
||||
<!-- Context: Text label when old Chrome version installed -->
|
||||
|
|
@ -38,11 +38,11 @@
|
|||
<!-- Context: Clear Text dialog -->
|
||||
<string name="all_text_will_be_cleared" comment="Erase all the text">\nΤὸ κείμενο θὰ ἐκκαθαρισθεῖ πλήρως\n</string>
|
||||
<!-- Context: Get Started menu -->
|
||||
<string name="get_started" comment="Menu for getting started">Μικρύνετε τὸ κείμενο</string>
|
||||
<string name="get_started" comment="Menu for getting started">Ξεκινῆστε</string>
|
||||
<!-- Context: Get Started menu -->
|
||||
<string name="add_a_keyboard" comment="Menu item to add a keyboard">Προσθέστε πληκτρολόγιο γιὰ τὴν γλῶσσα σας</string>
|
||||
<!-- Context: Get Started menu -->
|
||||
<string name="enable_system_keyboard" comment="Menu item to enable Keyman as system keyboard">Ὁρίστε τὸ Κῆμαν ὡς παν-συστημικὸ πληκτρολόγιο</string>
|
||||
<string name="enable_system_keyboard" comment="Menu item to enable Keyman as system keyboard">Ὁρίστε τὸ Keyman ὡς πληκτρολόγιο συστήματος</string>
|
||||
<!-- Context: Get Started menu -->
|
||||
<string name="set_keyman_as_default" comment="Menu item to set Keyman as default system keyboard">Ὁρίστε τὸ Κῆμαν ὡς προεπιλεγμένο πληκτρολόγιο</string>
|
||||
<!-- Context: Get Started menu -->
|
||||
|
|
@ -68,6 +68,8 @@
|
|||
<!-- Context: Keyman Settings menu -->
|
||||
<string name="adjust_keyboard_height" comment="Menu action to adjust keyboard height">Μεταβολὴ ὕψους πληκτρολογίου</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<string name="adjust_longpress_delay" comment="Menu action to adjust longpress delay duration">Καθυστέρηση παρατεταμένου πατήματος</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<string name="spacebar_caption" comment="Menu action to set the spacebar caption">Λεζάντα πλήκτρου διαστήματος</string>
|
||||
<!-- Context: Keyman Settings menu / Spacebar caption -->
|
||||
<string name="spacebar_caption_keyboard" comment="Spacebar caption option - keyboard">Πληκτρολόγιο</string>
|
||||
|
|
@ -87,11 +89,11 @@
|
|||
<string name="spacebar_caption_hint_blank" comment="Spacebar caption hint - blank">Καμμία λεζάντα στὸ πλῆκτρο διαστήματος</string>
|
||||
<!-- Context: Keyman Settings menu / Haptic feedback -->
|
||||
<string name="haptic_feedback" comment="haptic feedback on key press">Δὸνηση κατὰ τὴν πληκτρολόγηση</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<!-- Context: Keyman Settings menu. Removed in Keyman 18 -->
|
||||
<string name="show_banner" comment="text suggestions banner">Νὰ ἐμφανίζεται πάντα banner</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<!-- Context: Keyman Settings menu. Removed in Keyman 18 -->
|
||||
<string name="show_banner_on" comment="Description when toggle is on">Πρὸς ὑλοποίησιν</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<!-- Context: Keyman Settings menu. Removed in Keyman 18 -->
|
||||
<string name="show_banner_off" comment="Description when toggle is off">OFF: ἐμφάνιση μόνον ὅταν ἔχει ἐνεργοποιηθεῖ ἡ πρόβλεψη κειμένου</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<string name="show_send_crash_report" comment="permission for sending crash reports">Ἐπιτρέψτε τὴν ἀποστολὴ ἀναφορῶν κατάρρευσης μέσῳ δικτύου</string>
|
||||
|
|
@ -123,6 +125,14 @@
|
|||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Περιστρέψτε τὴν συσκευὴ γιὰ προσαρμογὴ τοῦ ὕψους σὲ λειτουργία πορτραίτου καὶ τοπίου</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Ἐπαναφέρετε τὶς προεπιλεγμένες ρυθμίσεις</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
<string name="longpress_delay_time" comment="Current longpress delay time (seconds)">Χρόνος καθυστέρησης: %1$.1f δευτ</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
<string name="ic_delay_time_up" comment="Make longpress delay longer">Μεγαλύτερος χρόνος καθυστέρησης</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
<string name="ic_delay_time_down" comment="Make longpress delay shorter">Μικρότερος χρόνος καθυστέρησης</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
<string name="ic_longpress_delay_slider" comment="Adjust longpress delay time">Ρυθμιστής καθυστέρησης πατήματος</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
<string name="hint_text" comment="Prompt to type in the browser search bar">Ἀναζητῆστε ἢ πληκτρολογῆστε URL</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@
|
|||
<!-- Context: KMP Package strings -->
|
||||
<string name="no_new_predictive_text_to_install" comment="Notification when no dictionaries can be installed">No new predictive text to install</string>
|
||||
<!-- Context: KMP Package strings -->
|
||||
<string name="no_targets_to_install" comment="Notification when a package doesn't contain keyboards or dictionaries">No keyboards or predictive text to install</string>
|
||||
<string name="no_targets_to_install" comment="Notification when a package doesn't contain keyboards or dictionaries">This is not a valid Keyman keyboard or predictive text package.</string>
|
||||
<!-- Context: KMP Package strings -->
|
||||
<string name="no_associated_languages" comment="Notification when a keyboard package doesn't contain languages">Keyboard package has no associated languages to install</string>
|
||||
<!-- Context: KMP Package strings -->
|
||||
|
|
|
|||
|
|
@ -67,6 +67,8 @@
|
|||
<!-- Context: Keyman Settings menu -->
|
||||
<string name="adjust_keyboard_height" comment="Menu action to adjust keyboard height">ချိၚ်ဆသမၠုၚ်ကဳဗုဒ်ညိ</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<string name="adjust_longpress_delay" comment="Menu action to adjust longpress delay duration">ချိၚ်အခိၚ် ဍဵုလအ်အ်</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<string name="spacebar_caption" comment="Menu action to set the spacebar caption">က္တိုပ်လိက် Spacebar</string>
|
||||
<!-- Context: Keyman Settings menu / Spacebar caption -->
|
||||
<string name="spacebar_caption_keyboard" comment="Spacebar caption option - keyboard">ကဳဗုဒ်</string>
|
||||
|
|
@ -87,11 +89,11 @@
|
|||
<string name="spacebar_caption_hint_blank" comment="Spacebar caption hint - blank">လ္ပဓမံက်ထ္ၜးမလိက်ပ္ဍဲspacebarညိ</string>
|
||||
<!-- Context: Keyman Settings menu / Haptic feedback -->
|
||||
<string name="haptic_feedback" comment="haptic feedback on key press">ပ္ဍဲကာလမတက်လိက် ချဳဒတဝ်ညိ</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<!-- Context: Keyman Settings menu. Removed in Keyman 18 -->
|
||||
<string name="show_banner" comment="text suggestions banner">ဓမံက်ထ္ၜးbannerလၟိုန်ညိ</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<!-- Context: Keyman Settings menu. Removed in Keyman 18 -->
|
||||
<string name="show_banner_on" comment="Description when toggle is on">သွက်ဂွံဓမံက်ရုပ်ရဴ</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<!-- Context: Keyman Settings menu. Removed in Keyman 18 -->
|
||||
<string name="show_banner_off" comment="Description when toggle is off">ကၟာတ်လဝ်မ္ဂး မလိက်ချိၚ်ဆလဝ်တံဂှ် ထ္ၜးအဃောပံက်လဝ်ဟေၚ်</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<string name="show_send_crash_report" comment="permission for sending crash reports">ကဵုအခေါၚ် ပၠံၚ်လိက်စဳရေၚ်လ္တူ အေန်တာနက်ညိ</string>
|
||||
|
|
@ -123,6 +125,14 @@
|
|||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">ဂွံချိၚ် လံက်သတိက် လံက်ဇမၠိၚ်ဂှ် ဗဂေတ်ကဵုစက်ပိုဲညိ။</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">ကလေၚ်သ္ပနူပကတိ</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
<string name="longpress_delay_time" comment="Current longpress delay time (seconds)">အခိၚ်ဖအိၚ်: %1$.1f စက္က</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
<string name="ic_delay_time_up" comment="Make longpress delay longer">ဖအိၚ်အခိၚ်လအ်အ်</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
<string name="ic_delay_time_down" comment="Make longpress delay shorter">ဖအိၚ်အခိၚ်အောန်န်</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
<string name="ic_longpress_delay_slider" comment="Adjust longpress delay time">ဍဵုလအ်အ်သွက် မသရေတ််ဖအိၚ်အခိၚ်</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
<string name="hint_text" comment="Prompt to type in the browser search bar">ဂၠာဲ ဟွံသေၚ် တက် URL</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
|
|
|
|||
173
android/KMAPro/kMAPro/src/main/res/values-vi-rVN/strings.xml
Normal file
173
android/KMAPro/kMAPro/src/main/res/values-vi-rVN/strings.xml
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- app_name removed in 16.0 beta. Set in build.gradle -->
|
||||
<!-- Context: Menu Action -->
|
||||
<string name="action_share" comment="Menu action to send text content to another app">Chia sẻ</string>
|
||||
<!-- Context: Menu Action -->
|
||||
<string name="action_web" comment="Menu action to open Keyman browser">Trình duyệt</string>
|
||||
<!-- Context: Menu Action -->
|
||||
<string name="action_text_size" comment="Menu action to adjust text size">Cỡ chữ</string>
|
||||
<!-- Context: Menu Action -->
|
||||
<string name="action_overflow" comment="Show additional menu items">Thêm</string>
|
||||
<!-- Context: Menu Action -->
|
||||
<string name="action_clear_text" comment="Menu action to erase text">Xóa văn bản</string>
|
||||
<!-- Context: Menu Action -->
|
||||
<string name="action_info" comment="Menu action to display Keyman help page">Thông tin</string>
|
||||
<!-- Context: Menu Action -->
|
||||
<string name="action_settings" comment="Menu action to open Settings menu">Cài đặt</string>
|
||||
<!-- Context: Menu Action -->
|
||||
<string name="action_install_updates" comment="Menu notification that keyboard or dictionary updates available">Cài đặt Cập nhật</string>
|
||||
<!-- Context: Title -->
|
||||
<string name="title_version" comment="Title of Keyman for Android version">Phiên bản: %1$s</string>
|
||||
<!-- Context: Text label when old Chrome version installed -->
|
||||
<string name="text_require_chrome_57" comment="Banner text when old version of Chrome is installed">Keyman cần trình duyệt Chrome phiên bản 57 hoặc mới hơn.</string>
|
||||
<!-- Context: Button label -->
|
||||
<string name="button_update_chrome" comment="Prompt to upgrade Chrome in the Play Store">Cập nhật Chrome</string>
|
||||
<!-- Context: Textfield prompt (… is the ellipsis character "...") -->
|
||||
<string name="textview_hint" comment="Prompt to start typing">Bắt đầu nhập tại đây…</string>
|
||||
<!-- Context: Splash screen (version/copyright info currently disabled -->
|
||||
<!-- Context: Splash screen (version/copyright info currently disabled -->
|
||||
<!-- Context: Text Size dialog -->
|
||||
<string name="text_size" comment="Current text size (number)">Cỡ chữ: %1$d</string>
|
||||
<!-- Context: Text Size dialog -->
|
||||
<string name="ic_text_size_up" comment="Make text bigger">Tăng cỡ chữ</string>
|
||||
<!-- Context: Text Size dialog -->
|
||||
<string name="ic_text_size_down" comment="Make text smaller">Giảm cỡ chữ</string>
|
||||
<!-- Context: Text Size dialog -->
|
||||
<string name="ic_text_size_slider" comment="Adjust text size">Thanh trượt kích thước văn bản</string>
|
||||
<!-- Context: Clear Text dialog -->
|
||||
<string name="all_text_will_be_cleared" comment="Erase all the text">\nTất cả văn bản sẽ bị xóa\n</string>
|
||||
<!-- Context: Get Started menu -->
|
||||
<string name="get_started" comment="Menu for getting started">Bắt đầu</string>
|
||||
<!-- Context: Get Started menu -->
|
||||
<string name="add_a_keyboard" comment="Menu item to add a keyboard">Thêm bàn phím cho ngôn ngữ của bạn</string>
|
||||
<!-- Context: Get Started menu -->
|
||||
<string name="enable_system_keyboard" comment="Menu item to enable Keyman as system keyboard">Đặt Keyman làm bàn phím hệ thống</string>
|
||||
<!-- Context: Get Started menu -->
|
||||
<string name="set_keyman_as_default" comment="Menu item to set Keyman as default system keyboard">Đặt Keyman làm bàn phím mặc định</string>
|
||||
<!-- Context: Get Started menu -->
|
||||
<string name="more_info" comment="Open the Keyman for Android help page">Thêm thông tin</string>
|
||||
<!-- Context: Get Started menu -->
|
||||
<string name="show_get_started" comment="Show the "Get Started" menu on startup">Hiển thị \"%1$s\" khi khởi động</string>
|
||||
<!-- Context: Android Storage Permission -->
|
||||
<string name="request_storage_permission" comment="Request storage permission to access keyboard packages">Để cài đặt bàn phím, hãy cho phép Keyman đọc bộ nhớ ngoài</string>
|
||||
<!-- Context: Android Storage Permission -->
|
||||
<string name="storage_permission_denied" comment="Keyboard package installation may fail since Android storage permission not granted">Yêu cầu cho phép bộ nhớ đã bị từ chối. Có khả năng không thể cài đặt bàn phím</string>
|
||||
<string name="storage_permission_denied2" comment="Keyboard package installation failed. Recommend install from local file"> Yêu cầu cấp quyền bộ nhớ không thành công. Hãy vào cài đặt trong Keyman - Cài đặt từ tệp cục bộ</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<string name="keyman_settings" comment="Settings menu title">Cài đặt</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<plurals name="installed_languages">
|
||||
<item quantity="other">Ngôn ngữ đã cài đặt (%1$d)</item>
|
||||
</plurals>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<string name="install_keyboard_or_dictionary" comment="Menu item to install keyboard or dictionary">Cài đặt Bàn phím hay Từ điển</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<string name="change_display_language" comment="Menu action to change interface language">Ngôn ngữ hiển thị</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<string name="adjust_keyboard_height" comment="Menu action to adjust keyboard height">Điều chỉnh độ cao bàn phím</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<string name="adjust_longpress_delay" comment="Menu action to adjust longpress delay duration">Điều chỉnh độ trễ khi nhấn lâu</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<string name="spacebar_caption" comment="Menu action to set the spacebar caption">Tên phím khoảng cách</string>
|
||||
<!-- Context: Keyman Settings menu / Spacebar caption -->
|
||||
<string name="spacebar_caption_keyboard" comment="Spacebar caption option - keyboard">Bàn phím</string>
|
||||
<!-- Context: Keyman Settings menu / Spacebar caption -->
|
||||
<string name="spacebar_caption_language" comment="Spacebar caption option - language">Ngôn ngữ</string>
|
||||
<!-- Context: Keyman Settings menu / Spacebar caption -->
|
||||
<string name="spacebar_caption_language_keyboard" comment="Spacebar caption option - language + keyboard">Ngôn ngữ + Bàn phím</string>
|
||||
<!-- Context: Keyman Settings menu / Spacebar caption -->
|
||||
<string name="spacebar_caption_blank" comment="Spacebar caption option - blank">Trống</string>
|
||||
<!-- Context: Keyman Settings menu / Spacebar caption hint -->
|
||||
<string name="spacebar_caption_hint_keyboard" comment="Spacebar caption hint - keyboard">Hiển thị tên bàn phím trên phím cách</string>
|
||||
<!-- Context: Keyman Settings menu / Spacebar caption hint -->
|
||||
<string name="spacebar_caption_hint_language" comment="Spacebar caption hint - language">Hiển thị tên ngôn ngữ trên phím cách</string>
|
||||
<!-- Context: Keyman Settings menu / Spacebar caption hint -->
|
||||
<string name="spacebar_caption_hint_language_keyboard" comment="Spacebar caption hint - language + keyboard">Ngôn ngữ và bàn phím trên phím cách</string>
|
||||
<!-- Context: Keyman Settings menu / Spacebar caption hint -->
|
||||
<string name="spacebar_caption_hint_blank" comment="Spacebar caption hint - blank">Không hiển thị gì trên phím cách</string>
|
||||
<!-- Context: Keyman Settings menu / Haptic feedback -->
|
||||
<string name="haptic_feedback" comment="haptic feedback on key press">Rung khi đánh máy</string>
|
||||
<!-- Context: Keyman Settings menu. Removed in Keyman 18 -->
|
||||
<string name="show_banner" comment="text suggestions banner">Luôn hiển thị dòng biểu ngữ</string>
|
||||
<!-- Context: Keyman Settings menu. Removed in Keyman 18 -->
|
||||
<string name="show_banner_on" comment="Description when toggle is on">Sắp được cài đặt</string>
|
||||
<!-- Context: Keyman Settings menu. Removed in Keyman 18 -->
|
||||
<string name="show_banner_off" comment="Description when toggle is off">Khi tắt, chỉ hiển thị khi bật tính năng dự đoán chữ</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<string name="show_send_crash_report" comment="permission for sending crash reports">Cho phép gửi báo cáo vấn đề qua mạng</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<string name="show_send_crash_report_on" comment="Description when toggle is on">Khi mở, báo cáo vấn đề sẽ được gửi đi</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<string name="show_send_crash_report_off" comment="Description when toggle is off">Khi tắt, báo cáo vấn đề sẽ không được gửi đi</string>
|
||||
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
|
||||
<string name="install_from_keyman_dot_com" comment="Install package from Keyman server">Cài đặt từ keyman.com</string>
|
||||
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
|
||||
<string name="install_from_local_file" comment="Install package file from local device">Cài đặt từ tệp cục bộ</string>
|
||||
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
|
||||
<string name="install_from_other_device" comment="Scan QR Code">Cài đặt từ thiết bị khác</string>
|
||||
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
|
||||
<string name="add_languages_to_installed_keyboard" comment="Install additional languages from a keyboard package">Thêm ngôn ngữ cho bàn phím đã cài đặt</string>
|
||||
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
|
||||
<string name="add_language_subtext" comment="Additional information for adding another language">(từ gói bàn phím)</string>
|
||||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="title_select_keyboard_package_list" comment="Select a keyboard package to add languages">Chọn Gói bàn phím</string>
|
||||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="title_select_languages_for_package" comment="Select language names from the keyboard package">Chọn ngôn ngữ cho %1$s</string>
|
||||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="added_language_to_keyboard" comment="Notification that a language name is added to a keyboard">Đã thêm ngôn ngữ \'%1$s\' vào %2$s</string>
|
||||
<!-- Context: Select Package and Select Languages menu -->
|
||||
<string name="all_languages_installed" comment="Text when all languages from a keyboard package have been installed">Tất cả ngôn ngữ đã được cài đặt rồi</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="drag_the_keyboard" comment="First line of adjusting keyboard height">Kéo bàn phím để điều chỉnh độ cao</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="rotate_the_device" comment="Second line of adjusting keyboard height">Xoay thiết bị để điều chỉnh độ ngang độ dọc</string>
|
||||
<!-- Context: Adjust Keyboard Height menu -->
|
||||
<string name="reset_to_defaults" comment="Button to reset to default heights">Khôi phục mặc định</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
<string name="longpress_delay_time" comment="Current longpress delay time (seconds)">Thời gian trễ: %1$.1f giây</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
<string name="ic_delay_time_up" comment="Make longpress delay longer">Thời gian trễ lâu hơn</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
<string name="ic_delay_time_down" comment="Make longpress delay shorter">Thời gian trễ thấp hơn</string>
|
||||
<!-- Context: Adjust Longpress Delay Time menu -->
|
||||
<string name="ic_longpress_delay_slider" comment="Adjust longpress delay time">Thanh trượt thời gian trễ nhấn lâu</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
<string name="hint_text" comment="Prompt to type in the browser search bar">Tìm kiếm hoặc nhập URL</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
<string name="title_bookmarks" comment="Title for bookmarks">Dấu trang</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
<string name="no_bookmarks" comment="Text when bookmark list is empty">Không có dấu trang</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
<string name="add_bookmark" comment="Confirmation to add bookmark">Thêm dấu trang</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
<string name="hint_title" comment="Page title for saved bookmark">Tiêu đề</string>
|
||||
<!-- Context: Keyman Web Browser -->
|
||||
<string name="hint_url" comment="Page URL for saved bookmark">url</string>
|
||||
<!-- Context: KMP Package strings -->
|
||||
<string name="title_package_failed_to_install" comment="Error dialog title when package failed to install">Cài đặt gói %1$s không thành công</string>
|
||||
<!-- Context: KMP Package strings -->
|
||||
<string name="downloading_keyboard_package" comment="Confirmation to download keyboard package filename">Đang tải xuống gói bàn phím \n%1$s…</string>
|
||||
<!-- Context: KMP Package strings -->
|
||||
<string name="failed_to_extract" comment="Notification that keyboard package failed to unzip">Giải nén thất bại</string>
|
||||
<!-- Context: KMP Package strings -->
|
||||
<string name="install_keyboard_package" comment="Title to install keyboard package">Cài đặt bàn phím</string>
|
||||
<!-- Context: KMP Package strings -->
|
||||
<string name="install_predictive_text_package" comment="Title to install dictionary package">Cài đặt từ điển</string>
|
||||
<!-- Context: KMP Package strings -->
|
||||
<string name="not_valid_package_file" comment="Notification when invalid package file cannot be installed">%1$s không phải tệp gói bàn phím Keyman hợp lệ.\n%2$s</string>
|
||||
<!-- Context: KMP Package strings -->
|
||||
<string name="no_new_touch_keyboards_to_install" comment="Notification when no touch-optimized keyboards can be installed">Gói bàn phím không có bàn phím được tối ưu hóa để cài đặt</string>
|
||||
<!-- Context: KMP Package strings -->
|
||||
<string name="no_new_predictive_text_to_install" comment="Notification when no dictionaries can be installed">Không có văn bản dự đoán mới để cài đặt</string>
|
||||
<!-- Context: KMP Package strings -->
|
||||
<string name="no_targets_to_install" comment="Notification when a package doesn't contain keyboards or dictionaries">Không có bàn phím hay văn bản dự đoán để cài đặt</string>
|
||||
<!-- Context: KMP Package strings -->
|
||||
<string name="no_associated_languages" comment="Notification when a keyboard package doesn't contain languages">Gói bàn phím không có ngôn ngữ liên quan để cài đặt</string>
|
||||
<!-- Context: KMP Package strings -->
|
||||
<string name="invalid_metadata" comment="kmp.json metadata file is invalid or missing in package">Siêu dữ liệu trong gói không hợp lệ/bị thiếu</string>
|
||||
<!-- Context: KMP Package strings -->
|
||||
<string name="minimum_keyboard_version_not_supported" comment="Notification when keyboard has functionality not supported by current Keyman">Bàn phím cần phiên bản mới hơn của ứng dụng Keyman</string>
|
||||
<!-- Context: anywhere -->
|
||||
<string name="unable_to_open_browser" comment="Notification when a browser activity cannot be launched">Không thể mở trình duyệt</string>
|
||||
</resources>
|
||||
|
|
@ -27,7 +27,6 @@
|
|||
<!-- Context: Menu Action -->
|
||||
<string name="action_install_updates" comment="Menu notification that keyboard or dictionary updates available">Install Updates</string>
|
||||
|
||||
|
||||
<!-- Context: Title -->
|
||||
<string name="title_version" comment="Title of Keyman for Android version">Version: %1$s</string>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@
|
|||
</intent>
|
||||
</queries>
|
||||
|
||||
<queries>
|
||||
<package android:name="com.android.webkit" />
|
||||
<package android:name="com.google.android.ims" />
|
||||
</queries>
|
||||
|
||||
<!-- https://developer.android.com/develop/ui/views/notifications/notification-permission#declare -->
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ var bannerImagePath = '';
|
|||
var bannerHTMLContents = '';
|
||||
var fragmentToggle = 0;
|
||||
var deferredBannerCall;
|
||||
var longpressDelay = null;
|
||||
|
||||
var sentryManager = new KeymanSentryManager({
|
||||
hostPlatform: "android"
|
||||
|
|
@ -71,6 +72,10 @@ function init() {
|
|||
|
||||
keyman.refreshOskLayout();
|
||||
}
|
||||
// Initialize the longpress delay
|
||||
if(longpressDelay !== null) {
|
||||
setLongpressDelay(longpressDelay);
|
||||
}
|
||||
});
|
||||
|
||||
keyman.addEventListener('keyboardloaded', setIsChiral);
|
||||
|
|
@ -128,11 +133,10 @@ function notifyHost(event, params) {
|
|||
// Update the KeymanWeb longpress delay
|
||||
// delay is in milliseconds
|
||||
function setLongpressDelay(delay) {
|
||||
if (keyman.osk) {
|
||||
longpressDelay = delay;
|
||||
if (keyman && keyman.osk) {
|
||||
keyman.osk.gestureParams.longpress.waitLength = delay;
|
||||
console_debug('setLongpressDelay('+delay+')');
|
||||
} else {
|
||||
window.console.log('setLongpressDelay error: keyman.osk undefined');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ public class DisplayLanguages {
|
|||
new DisplayLanguageType("ru-RU", "Pyccĸий (Russian)"),
|
||||
new DisplayLanguageType("shu-latn", "Shuwa (Latin)"),
|
||||
new DisplayLanguageType("sv-SE", "svenska (Swedish)"),
|
||||
new DisplayLanguageType("vi-VN", "Tiếng Việt (Vietnamese)"),
|
||||
new DisplayLanguageType("uk-UA", "Українська (Ukrainian)"),
|
||||
new DisplayLanguageType("hia-NG", "Waha"),
|
||||
new DisplayLanguageType("zh-CN", "中文(简体) (Simplified Chinese)")
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import android.content.res.Configuration;
|
|||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.AndroidRuntimeException;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
|
|
@ -110,14 +111,29 @@ final class KMKeyboard extends WebView {
|
|||
|
||||
public String specialOskFont = "";
|
||||
|
||||
public KMKeyboard(Context context) {
|
||||
/**
|
||||
* Constructor for KMKeyboard.
|
||||
* If System WebView not installed/enabled, will throw AndroidRuntimeException.
|
||||
* Can't catch the exception here because super(context) must be the first line.
|
||||
* @param context
|
||||
* @throws AndroidRuntimeException
|
||||
*/
|
||||
public KMKeyboard(Context context) throws AndroidRuntimeException {
|
||||
super(context);
|
||||
this.context = context;
|
||||
this.keyboardType = KeyboardType.KEYBOARD_TYPE_INAPP;
|
||||
initKMKeyboard(context);
|
||||
}
|
||||
|
||||
public KMKeyboard(Context context, KeyboardType keyboardType) {
|
||||
/**
|
||||
* Constructor for KMKeyboard.
|
||||
* If System WebView not installed/enabled, will throw AndroidRuntimeException.
|
||||
* Can't catch the exception here because super(context) must be the first line.
|
||||
* @param context {Context}
|
||||
* @param keyboardType {KeyboardType}
|
||||
* @throws AndroidRuntimeException
|
||||
*/
|
||||
public KMKeyboard(Context context, KeyboardType keyboardType) throws AndroidRuntimeException {
|
||||
super(context);
|
||||
this.context = context;
|
||||
this.keyboardType = keyboardType;
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ public class KMKeyboardDownloaderActivity extends BaseActivity {
|
|||
|
||||
//TODO: move to keyboard manager class
|
||||
private static ArrayList<KeyboardEventHandler.OnKeyboardDownloadEventListener> kbDownloadEventListeners = null;
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
|
|
|||
|
|
@ -119,7 +119,9 @@ public class KMKeyboardJSHandler {
|
|||
|
||||
InputConnection ic = KMManager.getInputConnection(k.keyboardType);
|
||||
if (ic == null) {
|
||||
KMLog.LogError(TAG, "insertText failed: InputConnection is null");
|
||||
// current active InputConnection is no longer bound to the Keyman Engine input method
|
||||
// (user switched to another IME) so disregard the keystroke.
|
||||
// https://developer.android.com/reference/android/inputmethodservice/InputMethodService#getCurrentInputConnection()
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -273,7 +275,9 @@ public class KMKeyboardJSHandler {
|
|||
|
||||
InputConnection ic = KMManager.getInputConnection(k.keyboardType);
|
||||
if (ic == null) {
|
||||
KMLog.LogError(TAG, "dispatchKey failed: InputConnection is null");
|
||||
// current active InputConnection is no longer bound to the Keyman Engine input method
|
||||
// (user switched to another IME) so disregard the keystroke.
|
||||
// https://developer.android.com/reference/android/inputmethodservice/InputMethodService#getCurrentInputConnection()
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import android.net.NetworkInfo;
|
|||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.text.InputType;
|
||||
import android.util.AndroidRuntimeException;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
|
@ -73,6 +74,7 @@ import com.keyman.engine.util.KMLog;
|
|||
import com.keyman.engine.util.KMString;
|
||||
import com.keyman.engine.util.MapCompat;
|
||||
import com.keyman.engine.util.WebViewUtils;
|
||||
import com.keyman.engine.util.WebViewUtils.SystemWebViewStatus;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
|
@ -687,12 +689,33 @@ public final class KMManager {
|
|||
KMKeyboardWebViewClient webViewClient = null;
|
||||
|
||||
if (keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP && InAppKeyboard == null) {
|
||||
InAppKeyboard = new KMKeyboard(appContext, KeyboardType.KEYBOARD_TYPE_INAPP);
|
||||
try {
|
||||
InAppKeyboard = new KMKeyboard(appContext, KeyboardType.KEYBOARD_TYPE_INAPP);
|
||||
} catch (AndroidRuntimeException e) {
|
||||
// Catch fatal error when WebView not installed/enabled
|
||||
// Only log exceptions unrelated to WebView
|
||||
if (e != null && !e.getMessage().contains("MissingWebViewPackageException")) {
|
||||
KMLog.LogException(TAG, "initKeyboard for INAPP" , e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
InAppKeyboardWebViewClient = new KMKeyboardWebViewClient(appContext, keyboardType);
|
||||
keyboard = InAppKeyboard;
|
||||
webViewClient = InAppKeyboardWebViewClient;
|
||||
} else if (keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM && SystemKeyboard == null) {
|
||||
SystemKeyboard = new KMKeyboard(appContext, KeyboardType.KEYBOARD_TYPE_SYSTEM);
|
||||
try {
|
||||
SystemKeyboard = new KMKeyboard(appContext, KeyboardType.KEYBOARD_TYPE_SYSTEM);
|
||||
} catch (AndroidRuntimeException e) {
|
||||
// Catch fatal error when WebView not installed/enabled
|
||||
// Only log exceptions unrelated to WebView
|
||||
if (e != null && !e.getMessage().contains("MissingWebViewPackageException")) {
|
||||
KMLog.LogException(TAG, "initKeyboard for SYSTEM", e);
|
||||
}
|
||||
// Direct user to pick another system keyboard
|
||||
InputMethodManager imManager = (InputMethodManager) appContext.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imManager.showInputMethodPicker();
|
||||
return;
|
||||
}
|
||||
SystemKeyboardWebViewClient = new KMKeyboardWebViewClient(appContext, keyboardType);
|
||||
keyboard = SystemKeyboard;
|
||||
webViewClient = SystemKeyboardWebViewClient;
|
||||
|
|
@ -722,6 +745,7 @@ public final class KMManager {
|
|||
}
|
||||
keyboard.setBanner(KMManager.BannerType.HTML);
|
||||
keyboard.showBanner(true);
|
||||
sendLongpressDelay();
|
||||
}
|
||||
setEngineWebViewVersionStatus(appContext, keyboard);
|
||||
}
|
||||
|
|
@ -768,6 +792,24 @@ public final class KMManager {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normally, launching the KeyboardPickerActivity from a system keyboard closes the parent app
|
||||
* (The keyboard picker becomes the root of the activity stack)
|
||||
* Calling this function keeps the parent app running in the background
|
||||
*/
|
||||
public static void dontCloseParentAppOnShowKeyboardPicker() {
|
||||
KeyboardPickerActivity.dontCloseParentAppOnShowKeyboardPicker();
|
||||
}
|
||||
|
||||
/**
|
||||
* Normally, launching the KeyboardPickerActivity from a system keyboard closes the parent app
|
||||
* (The keyboard picker becomes the root of the activity stack)
|
||||
* Calling this function restores this default behavior
|
||||
*/
|
||||
public static void closeParentAppOnShowKeyboardPicker() {
|
||||
KeyboardPickerActivity.closeParentAppOnShowKeyboardPicker();
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
public static View createInputView(InputMethodService inputMethodService) {
|
||||
//final Context context = appContext;
|
||||
|
|
@ -776,6 +818,10 @@ public final class KMManager {
|
|||
final FrameLayout mainLayout = new FrameLayout(appContext);
|
||||
mainLayout.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
|
||||
|
||||
if (SystemKeyboard == null) {
|
||||
return mainLayout;
|
||||
}
|
||||
|
||||
RelativeLayout keyboardLayout = new RelativeLayout(appContext);
|
||||
keyboardLayout.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
|
||||
ViewGroup parent = (ViewGroup) SystemKeyboard.getParent();
|
||||
|
|
@ -921,7 +967,7 @@ public final class KMManager {
|
|||
copyAsset(context, KMFilename_KmwCss, "", true);
|
||||
copyAsset(context, KMFilename_KmwGlobeHintCss, "", true);
|
||||
copyAsset(context, KMFilename_Osk_Ttf_Font, "", true);
|
||||
|
||||
|
||||
// Needed until our minimum version of Chrome is 61.0+.
|
||||
copyAsset(context, KMFilename_JSPolyfill2, "", true);
|
||||
|
||||
|
|
@ -2124,7 +2170,7 @@ public final class KMManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the longpress delay (in milliseconds) as a stored preference.
|
||||
* Set the longpress delay (in milliseconds) as a stored preference and sends to KeymanWeb.
|
||||
* Valid range is 300 ms to 1500 ms. Returns true if the preference is successfully stored.
|
||||
* @param longpressDelay - int longpress delay in milliseconds
|
||||
* @return boolean
|
||||
|
|
@ -2140,17 +2186,19 @@ public final class KMManager {
|
|||
editor.putInt(KMKey_LongpressDelay, longpressDelay);
|
||||
editor.commit();
|
||||
|
||||
// Send longpress delay to KeymanWeb
|
||||
sendLongpressDelay();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends options to the KeymanWeb keyboard.
|
||||
* Sends longpress delay to the KeymanWeb keyboard.
|
||||
* 1. number of milliseconds to trigger a longpress gesture.
|
||||
* This method requires a keyboard to be loaded for the value to take effect.
|
||||
*/
|
||||
public static void sendOptionsToKeyboard() {
|
||||
private static void sendLongpressDelay() {
|
||||
int longpressDelay = getLongpressDelay();
|
||||
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP)) {
|
||||
if (InAppKeyboard != null) {
|
||||
InAppKeyboard.loadJavascript(KMString.format("setLongpressDelay(%d)", longpressDelay));
|
||||
}
|
||||
|
||||
|
|
@ -2270,7 +2318,10 @@ public final class KMManager {
|
|||
|
||||
if (kbType == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
if (KeyboardPickerActivity.getCanCloseParentAppOnShowKeyboardPicker()) {
|
||||
// Keyboard Picker Activity becomes root activity and clears Keyman app
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
}
|
||||
}
|
||||
|
||||
i.putExtra(KMKey_DisplayKeyboardSwitcher, kbType == KeyboardType.KEYBOARD_TYPE_SYSTEM);
|
||||
|
|
@ -2301,9 +2352,9 @@ public final class KMManager {
|
|||
public static boolean updateText(KeyboardType kbType, String text) {
|
||||
boolean result = false;
|
||||
|
||||
if (kbType == KeyboardType.KEYBOARD_TYPE_INAPP) {
|
||||
if (kbType == KeyboardType.KEYBOARD_TYPE_INAPP && InAppKeyboard != null) {
|
||||
return InAppKeyboard.updateText(text);
|
||||
} else if (kbType == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
|
||||
} else if (kbType == KeyboardType.KEYBOARD_TYPE_SYSTEM && SystemKeyboard != null) {
|
||||
return SystemKeyboard.updateText(text);
|
||||
}
|
||||
|
||||
|
|
@ -2335,13 +2386,13 @@ public final class KMManager {
|
|||
public static boolean updateSelectionRange(KeyboardType kbType) {
|
||||
boolean result = false;
|
||||
|
||||
if (kbType == KeyboardType.KEYBOARD_TYPE_INAPP) {
|
||||
if (kbType == KeyboardType.KEYBOARD_TYPE_INAPP && InAppKeyboard != null) {
|
||||
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP) && !InAppKeyboard.shouldIgnoreSelectionChange()) {
|
||||
result = InAppKeyboard.updateSelectionRange();
|
||||
}
|
||||
|
||||
InAppKeyboard.setShouldIgnoreSelectionChange(false);
|
||||
} else if (kbType == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
|
||||
} else if (kbType == KeyboardType.KEYBOARD_TYPE_SYSTEM && SystemKeyboard != null) {
|
||||
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM) && !SystemKeyboard.shouldIgnoreSelectionChange()) {
|
||||
result = SystemKeyboard.updateSelectionRange();
|
||||
}
|
||||
|
|
@ -2354,11 +2405,11 @@ public final class KMManager {
|
|||
|
||||
public static void resetContext(KeyboardType kbType) {
|
||||
if (kbType == KeyboardType.KEYBOARD_TYPE_INAPP) {
|
||||
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP)) {
|
||||
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP) && InAppKeyboard != null) {
|
||||
InAppKeyboard.loadJavascript("resetContext()");
|
||||
}
|
||||
} else if (kbType == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
|
||||
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM)) {
|
||||
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM) && SystemKeyboard != null) {
|
||||
SystemKeyboard.loadJavascript("resetContext()");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ public final class KMTextView extends AppCompatEditText {
|
|||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
if (hasFocus) {
|
||||
activeView = v;
|
||||
if (KMManager.InAppKeyboardWebViewClient.getKeyboardLoaded()) {
|
||||
if (KMManager.InAppKeyboard != null && KMManager.InAppKeyboardWebViewClient.getKeyboardLoaded()) {
|
||||
updateTextContext();
|
||||
}
|
||||
showKeyboard();
|
||||
|
|
@ -165,7 +165,7 @@ public final class KMTextView extends AppCompatEditText {
|
|||
protected void onSelectionChanged(int selStart, int selEnd) {
|
||||
super.onSelectionChanged(selStart, selEnd);
|
||||
if (activeView != null && activeView.equals(this)) {
|
||||
if (KMManager.updateSelectionRange(KMManager.KeyboardType.KEYBOARD_TYPE_INAPP)) {
|
||||
if (KMManager.InAppKeyboard != null && KMManager.updateSelectionRange(KMManager.KeyboardType.KEYBOARD_TYPE_INAPP)) {
|
||||
KMManager.resetContext(KeyboardType.KEYBOARD_TYPE_INAPP);
|
||||
}
|
||||
}
|
||||
|
|
@ -176,7 +176,7 @@ public final class KMTextView extends AppCompatEditText {
|
|||
AppCompatActivity activity = (AppCompatActivity)context;
|
||||
|
||||
Window mainWindow = activity.getWindow();
|
||||
if (hasWindowFocus) {
|
||||
if (hasWindowFocus && KMManager.InAppKeyboard != null) {
|
||||
KMManager.InAppKeyboardWebViewClient.setContext(context);
|
||||
activeView = mainWindow.getCurrentFocus();
|
||||
|
||||
|
|
|
|||
|
|
@ -81,6 +81,14 @@ public final class KeyboardPickerActivity extends BaseActivity {
|
|||
}
|
||||
|
||||
private boolean dismissOnSelect = true;
|
||||
|
||||
/*
|
||||
// When the System Keyboard launches the keyboard picker while this flag is set,
|
||||
// the intent includes Intent.FLAG_ACTIVITY_CLEAR_TASK which clears the activity stack.
|
||||
// Disable this to keep the keyboard picker from clearing the parent app.
|
||||
*/
|
||||
private static boolean canCloseParentAppOnShowKeyboardPicker = true;
|
||||
|
||||
protected static boolean canAddNewKeyboard = true;
|
||||
protected static boolean canRemoveKeyboard = true;
|
||||
protected static boolean shouldCheckKeyboardUpdates = true;
|
||||
|
|
@ -474,6 +482,32 @@ public final class KeyboardPickerActivity extends BaseActivity {
|
|||
notifyKeyboardsUpdate(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns canCloseParentAppOnShowKeyboardPicker
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean getCanCloseParentAppOnShowKeyboardPicker() {
|
||||
return canCloseParentAppOnShowKeyboardPicker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normally, launching the KeyboardPickerActivity from a system keyboard closes the parent app
|
||||
* (The keyboard picker becomes the root of the activity stack)
|
||||
* Calling this function keeps the parent app running in the background
|
||||
*/
|
||||
protected static void dontCloseParentAppOnShowKeyboardPicker() {
|
||||
canCloseParentAppOnShowKeyboardPicker = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normally, launching the KeyboardPickerActivity from a system keyboard closes the parent app
|
||||
* (The keyboard picker becomes the root of the activity stack)
|
||||
* Calling this function restores this default behavior
|
||||
*/
|
||||
protected static void closeParentAppOnShowKeyboardPicker() {
|
||||
canCloseParentAppOnShowKeyboardPicker = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* getModelIDFromPosition - Get the lexical model ID at a given position
|
||||
* @param context
|
||||
|
|
|
|||
|
|
@ -5,11 +5,15 @@ import android.content.BroadcastReceiver;
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.keyman.engine.R;
|
||||
import com.keyman.engine.util.KMLog;
|
||||
import com.keyman.engine.util.DownloadFileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
|
|
@ -206,12 +210,34 @@ public class CloudDownloadMgr{
|
|||
* @param params the cloud api params for download
|
||||
* @param <ModelType> the target models type
|
||||
* @param <ResultType> the cloud requests result type
|
||||
* @return `false` if the download request cannot be executed; `true` otherwise.
|
||||
*/
|
||||
public <ModelType,ResultType> void executeAsDownload(Context aContext, String aDownloadIdentifier,
|
||||
public <ModelType,ResultType> boolean executeAsDownload(Context aContext, String aDownloadIdentifier,
|
||||
ModelType aTargetModel,
|
||||
ICloudDownloadCallback<ModelType,ResultType> aCallback,
|
||||
CloudApiTypes.CloudApiParam... params)
|
||||
{
|
||||
CloudApiTypes.CloudApiParam... params) {
|
||||
try {
|
||||
executeAsDownloadInternal(aContext, aDownloadIdentifier, aTargetModel, aCallback, params);
|
||||
} catch (DownloadManagerDisabledException e) {
|
||||
Toast.makeText(aContext,
|
||||
aContext.getString(R.string.update_check_download_manager_disabled),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(aContext,
|
||||
aContext.getString(R.string.update_check_unavailable),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
KMLog.LogException(TAG, "Unexpected exception occurred during download/query attempt", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private <ModelType,ResultType> void executeAsDownloadInternal(Context aContext, String aDownloadIdentifier,
|
||||
ModelType aTargetModel,
|
||||
ICloudDownloadCallback<ModelType,ResultType> aCallback,
|
||||
CloudApiTypes.CloudApiParam... params) throws DownloadManagerDisabledException {
|
||||
if(!isInitialized) {
|
||||
Log.w(TAG, "DownloadManager not initialized. Initializing CloudDownloadMgr.");
|
||||
initialize(aContext);
|
||||
|
|
@ -219,16 +245,22 @@ public class CloudDownloadMgr{
|
|||
KMLog.LogBreadcrumb("CloudDownloadMgr", "CloudDownloadMgr.executeAsDownload() called; already initialized", true);
|
||||
}
|
||||
|
||||
DownloadManager downloadManager = DownloadFileUtils.getDownloadManager(aContext);
|
||||
if(downloadManager == null) {
|
||||
// The callback object provided to us provides no way to directly signal a
|
||||
// failure. That said, we can also immediately detect that we WILL fail and
|
||||
// corresponding error _now_, rather than later.
|
||||
//
|
||||
// Unique custom error so it's easy to explicitly filter.
|
||||
throw new DownloadManagerDisabledException();
|
||||
}
|
||||
|
||||
synchronized (downloadSetByDownloadIdentifier) {
|
||||
|
||||
if (alreadyDownloadingData(aDownloadIdentifier) || params == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
DownloadManager downloadManager = (DownloadManager) aContext.getSystemService(Context.DOWNLOAD_SERVICE);
|
||||
if(downloadManager==null)
|
||||
throw new IllegalStateException("DownloadManager is not available");
|
||||
|
||||
aCallback.initializeContext(aContext);
|
||||
|
||||
CloudApiTypes.CloudDownloadSet<ModelType,ResultType> _downloadSet =
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
package com.keyman.engine.cloud;
|
||||
|
||||
public class DownloadManagerDisabledException extends RuntimeException {
|
||||
DownloadManagerDisabledException() {
|
||||
super("System service DownloadManager is not available and cannot facilitate downloads or queries.");
|
||||
}
|
||||
}
|
||||
|
|
@ -111,8 +111,8 @@ public class CloudLexicalModelMetaDataDownloadCallback implements ICloudDownload
|
|||
|
||||
BaseActivity.makeToast(aContext, R.string.dictionary_download_start_in_background, Toast.LENGTH_SHORT);
|
||||
|
||||
CloudDownloadMgr.getInstance().executeAsDownload(aContext,
|
||||
_r.additionalDownloadid, null, _callback,
|
||||
CloudDownloadMgr.getInstance().executeAsDownload(
|
||||
aContext, _r.additionalDownloadid, null, _callback,
|
||||
_r.additionalDownloads.toArray(new CloudApiTypes.CloudApiParam[0]));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,12 +13,14 @@ import com.keyman.engine.KMManager;
|
|||
import com.keyman.engine.KeyboardPickerActivity;
|
||||
import com.keyman.engine.R;
|
||||
import com.keyman.engine.cloud.CloudApiTypes;
|
||||
import com.keyman.engine.cloud.DownloadManagerDisabledException;
|
||||
import com.keyman.engine.cloud.impl.CloudCatalogDownloadCallback;
|
||||
import com.keyman.engine.cloud.impl.CloudCatalogDownloadReturns;
|
||||
import com.keyman.engine.cloud.CloudDataJsonUtil;
|
||||
import com.keyman.engine.cloud.CloudDownloadMgr;
|
||||
import com.keyman.engine.packages.JSONUtils;
|
||||
import com.keyman.engine.util.BCP47;
|
||||
import com.keyman.engine.util.DownloadFileUtils;
|
||||
import com.keyman.engine.util.KMLog;
|
||||
import com.keyman.engine.util.VersionUtils;
|
||||
|
||||
|
|
@ -366,6 +368,10 @@ public class CloudRepository {
|
|||
* @param onFailure A callback to be triggered upon failure of a query.
|
||||
*/
|
||||
private void downloadMetaDataFromServer(@NonNull Context context, UpdateHandler updateHandler, Runnable onSuccess, Runnable onFailure) {
|
||||
if(DownloadFileUtils.getDownloadManager(context) == null) {
|
||||
onFailure.run();
|
||||
return;
|
||||
}
|
||||
boolean cacheValid = getCacheValidity(context);
|
||||
|
||||
// For local and PR test builds, force download of metadata
|
||||
|
|
@ -405,8 +411,13 @@ public class CloudRepository {
|
|||
BaseActivity.makeToast(context, R.string.catalog_download_is_running_in_background, Toast.LENGTH_SHORT);
|
||||
} else {
|
||||
updateIsRunning = true;
|
||||
CloudDownloadMgr.getInstance().executeAsDownload(
|
||||
boolean executionStarted = CloudDownloadMgr.getInstance().executeAsDownload(
|
||||
context, DOWNLOAD_IDENTIFIER_CATALOGUE, memCachedDataset, _download_callback, params);
|
||||
if(!executionStarted) {
|
||||
// Since we couldn't initiate the execution's async components,
|
||||
// we need to immediately clear the update-running flag.
|
||||
updateIsRunning = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,6 +122,8 @@ public class ResourcesUpdateTool implements KeyboardEventHandler.OnKeyboardDownl
|
|||
return;
|
||||
}
|
||||
|
||||
// Warning: can be run by our system keyboard, which will attempt to
|
||||
// display the toast if it or the app is visible!
|
||||
BaseActivity.makeToast(currentContext, R.string.update_check_unavailable, Toast.LENGTH_SHORT);
|
||||
lastUpdateCheck = Calendar.getInstance();
|
||||
updateCheckFailed = true;
|
||||
|
|
|
|||
|
|
@ -122,8 +122,8 @@ public class PackageProcessor {
|
|||
*/
|
||||
public File unzipKMP(File path) throws IOException {
|
||||
File tempKeyboardPath = constructPath(path, true);
|
||||
if (!tempKeyboardPath.exists()) {
|
||||
tempKeyboardPath.mkdir();
|
||||
if (!tempKeyboardPath.exists() && !tempKeyboardPath.mkdir()) {
|
||||
KMLog.LogBreadcrumb(TAG, "Unable to mkdir for: " + tempKeyboardPath.toString(), true);
|
||||
}
|
||||
ZipUtils.unzip(path, tempKeyboardPath);
|
||||
|
||||
|
|
@ -165,7 +165,6 @@ public class PackageProcessor {
|
|||
JSONParser parser = new JSONParser();
|
||||
return parser.getJSONObjectFromFile(infoFile);
|
||||
} else {
|
||||
KMLog.LogError(TAG, "kmp.json does not exist");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -421,7 +420,6 @@ public class PackageProcessor {
|
|||
*/
|
||||
public String getPackageTarget(JSONObject json) {
|
||||
if (json == null) {
|
||||
KMLog.LogError(TAG, "kmp.json is null");
|
||||
return PP_TARGET_INVALID;
|
||||
}
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.keyman.engine.util;
|
|||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URL;
|
||||
|
||||
public final class Connection {
|
||||
|
|
@ -99,6 +100,9 @@ public final class Connection {
|
|||
attempt++;
|
||||
}
|
||||
}
|
||||
} catch (SocketTimeoutException e) {
|
||||
KMLog.LogBreadcrumb(TAG, "Connection.initialize timeout for: " + originalUrl, true);
|
||||
KMLog.LogInfo(TAG, "SocketTimeoutException");
|
||||
} catch (Exception e) {
|
||||
KMLog.LogException(TAG, "Initialization failed:", e);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@
|
|||
*/
|
||||
package com.keyman.engine.util;
|
||||
|
||||
import android.app.DownloadManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.OpenableColumns;
|
||||
|
|
@ -21,6 +23,37 @@ import java.io.InputStream;
|
|||
public final class DownloadFileUtils {
|
||||
private static final String TAG = "DownloadFileUtils";
|
||||
|
||||
private static final String DOWNLOAD_MANAGER_PACKAGE_NAME = "com.android.providers.downloads";
|
||||
|
||||
/**
|
||||
* Determines whether or not Android's `DownloadManager` service is active, only
|
||||
* returning an instance of DownloadManager when it is currently accessible and enabled.
|
||||
* Downloads and cloud queries are impossible when it's disabled.
|
||||
*
|
||||
* This solution is based heavily on
|
||||
* https://gist.github.com/Folyd/b9412bb6e2b06eb511f7.
|
||||
* @return A valid and enabled reference to the system's DownloadManager service. May be null
|
||||
* if it is not accessible or is disabled.
|
||||
*/
|
||||
public static DownloadManager getDownloadManager(Context context) {
|
||||
DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
|
||||
if(downloadManager==null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int state = context.getPackageManager().getApplicationEnabledSetting(DOWNLOAD_MANAGER_PACKAGE_NAME);
|
||||
|
||||
if(
|
||||
state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED ||
|
||||
state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER ||
|
||||
state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
|
||||
) {
|
||||
return null;
|
||||
};
|
||||
|
||||
return downloadManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Small class for returning information about a file downloaded via DownloadManager.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -4,9 +4,11 @@
|
|||
package com.keyman.engine.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.util.AndroidRuntimeException;
|
||||
import android.view.ViewGroup;
|
||||
import android.webkit.CookieManager;
|
||||
import android.webkit.WebView;
|
||||
import com.keyman.engine.util.KMLog;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
|
@ -22,6 +24,14 @@ public final class WebViewUtils {
|
|||
FULL; // WebView supports touch keyboards and LDML keyboards
|
||||
}
|
||||
|
||||
// System WebView status
|
||||
public enum SystemWebViewStatus {
|
||||
UNDETERMINED, // Functionality not determined yet
|
||||
NOT_INSTALLED, // WebView not installed
|
||||
DISABLED, // WebView installed, but disabled
|
||||
FULL; // WebView installed and enabled
|
||||
}
|
||||
|
||||
private static final String CHROME_INSTALL_PATTERN_FORMATSTR = "^.*Chrome/([\\d.]+).*$";
|
||||
private static final Pattern installPattern = Pattern.compile(CHROME_INSTALL_PATTERN_FORMATSTR);
|
||||
|
||||
|
|
@ -51,6 +61,34 @@ public final class WebViewUtils {
|
|||
return EngineWebViewVersionStatus.FULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the state of the system WebView (required for Keyman)
|
||||
* @param context
|
||||
* @return {SystemWebViewStatus}
|
||||
*/
|
||||
public static SystemWebViewStatus getSystemWebViewStatus(
|
||||
Context context) {
|
||||
// Check whether WebView is installed
|
||||
// If the package is installed but disabled, hasSystemFeature() still returns true
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
if (!packageManager.hasSystemFeature("android.software.webview")) {
|
||||
return SystemWebViewStatus.NOT_INSTALLED;
|
||||
}
|
||||
|
||||
// Use CookieManager to check if WebView is really enabled
|
||||
// https://stackoverflow.com/a/70354583
|
||||
try {
|
||||
CookieManager.getInstance();
|
||||
} catch (AndroidRuntimeException e) {
|
||||
// WebView installed but not enabled
|
||||
return SystemWebViewStatus.DISABLED;
|
||||
} catch (Exception e) {
|
||||
return SystemWebViewStatus.DISABLED;
|
||||
}
|
||||
|
||||
return SystemWebViewStatus.FULL;
|
||||
}
|
||||
|
||||
// Inject a meta viewport tag into the head of the file if it doesn't exist
|
||||
public static void injectViewport(WebView webView) {
|
||||
if (webView != null) {
|
||||
|
|
|
|||
|
|
@ -133,10 +133,15 @@
|
|||
<string name="keyboard_install_toast" comment="Notification when a keyboard is installed">Τὸ πληκτρολόγιο %1$s ἐγκατεστάθη</string>
|
||||
<!-- Context: Language Settings Keyboard uninstall strings -->
|
||||
<string name="keyboard_deleted_toast" comment="Notification when a keyboard is deleted">Τὸ πληκτρολόγιο διεγράφη</string>
|
||||
<!-- Context: Language Settings menu strings -->
|
||||
<!-- Context: Language Settings menu strings. Removed in Keyman 18 -->
|
||||
<string name="enable_corrections" comment="Enable corrections from the suggestion banner">Ἐνεργοποιῆστε τὶς διορθώσεις</string>
|
||||
<!-- Context: Language Settings menu strings -->
|
||||
<!-- Context: Language Settings menu strings. Removed in Keyman 18 -->
|
||||
<string name="enable_predictions" comment="Enable predictions from the suggestion banner">Ἐνεργοποιῆστε προβλέψεις κειμένου</string>
|
||||
<!-- Context: Language Settings menu strings - radio buttons -->
|
||||
<string name="suggestions_radio_0" comment="Predictions and corrections disabled">Ἀπενεργοποίηση προτάσεων</string>
|
||||
<string name="suggestions_radio_1" comment="Suggestions Enabled: Predictions with no corrections">Μόνο προβλέψεις</string>
|
||||
<string name="suggestions_radio_2" comment="Suggestions Enabled: Predictions with corrections">Προβλέψεις καὶ διορθώσεις</string>
|
||||
<string name="suggestions_radio_3" comment="Suggestions Enabled: Predictions with auto-corrections">Προβλέψεις μὲ αὐτόματες διορθώσεις</string>
|
||||
<!-- Context: Language Settings menu strings -->
|
||||
<string name="model_label">Λεξικό</string>
|
||||
<plurals name="model_count">
|
||||
|
|
|
|||
|
|
@ -131,10 +131,15 @@
|
|||
<string name="keyboard_install_toast" comment="Notification when a keyboard is installed">ကဳဗုဒ် %1$s စုတ်လဝ်တုဲ</string>
|
||||
<!-- Context: Language Settings Keyboard uninstall strings -->
|
||||
<string name="keyboard_deleted_toast" comment="Notification when a keyboard is deleted">ဇိုတ် ကဳဗုဒ်တုဲ</string>
|
||||
<!-- Context: Language Settings menu strings -->
|
||||
<!-- Context: Language Settings menu strings. Removed in Keyman 18 -->
|
||||
<string name="enable_corrections" comment="Enable corrections from the suggestion banner">ပံက် ပလေဝ်ဒါန်</string>
|
||||
<!-- Context: Language Settings menu strings -->
|
||||
<!-- Context: Language Settings menu strings. Removed in Keyman 18 -->
|
||||
<string name="enable_predictions" comment="Enable predictions from the suggestion banner">ပံက် တၚ်ချိၚ်ခယျဂမၠိုၚ်</string>
|
||||
<!-- Context: Language Settings menu strings - radio buttons -->
|
||||
<string name="suggestions_radio_0" comment="Predictions and corrections disabled">Disable suggestions</string>
|
||||
<string name="suggestions_radio_1" comment="Suggestions Enabled: Predictions with no corrections">တၚ်ချိၚ်ခယျဂမၠိုၚ်ဟေၚ်</string>
|
||||
<string name="suggestions_radio_2" comment="Suggestions Enabled: Predictions with corrections">တၚ်ချၚ်ခယျ ကေုာံ တၚ်ပလေဝ်ဂမၠိုၚ်</string>
|
||||
<string name="suggestions_radio_3" comment="Suggestions Enabled: Predictions with auto-corrections">တၚ်ချိၚ်ခယျနကဵု တၚ်ပလေဝ်အဝ်တဵု</string>
|
||||
<!-- Context: Language Settings menu strings -->
|
||||
<string name="model_label">အဘိဓာန်</string>
|
||||
<plurals name="model_count">
|
||||
|
|
|
|||
173
android/KMEA/app/src/main/res/values-vi-rVN/strings.xml
Normal file
173
android/KMEA/app/src/main/res/values-vi-rVN/strings.xml
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Device type -->
|
||||
<!-- Application name (Keyman Engine for Android) -->
|
||||
<!-- Context: Title for list -->
|
||||
<plurals name="title_keyboards">
|
||||
<item quantity="other">Bàn phím</item>
|
||||
</plurals>
|
||||
<!-- Context: Title for list -->
|
||||
<plurals name="title_other_input_methods">
|
||||
<item quantity="other">Bộ gõ khác</item>
|
||||
</plurals>
|
||||
<!-- Context: Title for list -->
|
||||
<string name="title_add_keyboard" comment="Add a new keyboard">Thêm bàn phím mới</string>
|
||||
<!-- Context: Title for list -->
|
||||
<string name="title_languages_settings" comment="List of installed languages">Ngôn ngữ đã cài đặt</string>
|
||||
<!-- Context: Title for list -->
|
||||
<string name="title_language_settings" comment="Keyman Settings for a language">Cài đặt %1$s</string>
|
||||
<!-- Context: Button label -->
|
||||
<string name="label_add" comment="Button to add an item">Thêm</string>
|
||||
<!-- Context: Button label -->
|
||||
<string name="label_back" comment="Button to go back">Quay lại</string>
|
||||
<!-- Context: Button label -->
|
||||
<string name="label_cancel" comment="Button to cancel an action">Hủy</string>
|
||||
<!-- Context: Button label -->
|
||||
<string name="label_close" comment="Close a dialog">Đóng</string>
|
||||
<!-- Context: Button label. Removed in Keyman 15 -->
|
||||
<string name="label_close_keyman" comment="Close the Keyman application">Đóng ứng dụng Keyman</string>
|
||||
<!-- Context: Button label -->
|
||||
<string name="label_forward" comment="Button to go forward">Chuyển tiếp</string>
|
||||
<!-- Context: Button label -->
|
||||
<string name="label_next_input_method" comment="Switch to next input method (replaces label_close_keyman)">Phương thức nhập kế tiếp</string>
|
||||
<!-- Context: Button label -->
|
||||
<string name="label_download" comment="Button to confirm downloading an item">Tải về</string>
|
||||
<!-- Context: Button label -->
|
||||
<string name="label_install" comment="Confirmation to install a package">Cài đặt</string>
|
||||
<!-- Context: Button label -->
|
||||
<string name="label_later" comment="Do an action at a later time">Để sau</string>
|
||||
<!-- Context: Button label -->
|
||||
<string name="label_next" comment="Go to the next screen">Tiếp theo</string>
|
||||
<!-- Context: Button label -->
|
||||
<string name="label_ok" comment="Button to acknowledge a dialog">Vâng</string>
|
||||
<!-- Context: Button label -->
|
||||
<string name="label_update" comment="Dialog button to update a resource">Cập nhật</string>
|
||||
<!-- Context: No internet for updates -->
|
||||
<string name="no_internet_connection" comment="Internet connection missing">Không có kết nối mạng</string>
|
||||
<!-- Context: Keyboard Updates -->
|
||||
<string name="cannot_connect" comment="Error message when network connection fails">Không thể kết nối tới máy chủ Keyman!</string>
|
||||
<!-- Context: Keyboard Updates -->
|
||||
<string name="confirm_delete_keyboard" comment="Confirmation to delete a keyboard">Bạn có muốn xóa bàn phím này không?</string>
|
||||
<!-- Context: Keyboard Updates -->
|
||||
<string name="confirm_download_keyboard" comment="Confirmation to download a keyboard update">Bạn có muốn tải xuống phiên bản mới nhất của bàn phím này?</string>
|
||||
<!-- Context: Keyboard Updates -->
|
||||
<string name="confirm_update" comment="Confirmation to update several resources">Bạn có muốn cập nhật bàn phím lẫn từ điển bây giờ không?</string>
|
||||
<!-- Context: Keyboard Updates -->
|
||||
<string name="keyboard_updates_channel">Cập nhật về Nguồn dữ liệu</string>
|
||||
<!-- Context: Keyboard Updates -->
|
||||
<string name="keyboard_updates_available" comment="Notification when resource updates are available">Có sẵn cập nhật về Nguồn dữ liệu</string>
|
||||
<!-- Context: Keyboard Updates -->
|
||||
<string name="update_available" comment="Currently installed version of a resource with an available update">%1$s (Đã có bản cập nhật)</string>
|
||||
<!-- Context: Keyboard Updates -->
|
||||
<string name="keyboard_update_message" comment="language name: keyboard name">Cập nhật có sẵn cho bàn phím %1$s: %2$s </string>
|
||||
<!-- Context: Keyboard Updates -->
|
||||
<string name="dictionary_update_message">Cập nhật có sẵn cho từ điển%1$s:%2$s </string>
|
||||
<!-- Context: Keyboard Info and Keyboard Settings -->
|
||||
<string name="keyboard_version" comment="Label for keyboard version">Phiên bản bàn phím</string>
|
||||
<!-- Context: Keyboard Info and Keyboard Settings -->
|
||||
<string name="help_link" comment="Label for help link">Link trợ giúp</string>
|
||||
<!-- Context: Keyboard Info and Keyboard Settings -->
|
||||
<string name="uninstall_keyboard" comment="Label for uninstalling keyboard">Gỡ cài đặt bàn phím</string>
|
||||
<!-- Context: Keyboard Info and Keyboard Settings -->
|
||||
<string name="keyboard_picker_new_keyboard" comment="Mark a language name that's newly installed in keyboard list">[new] %1$s</string>
|
||||
<!-- Context: Keyboard Info and Keyboard Settings -->
|
||||
<string name="keyboard_qr_code" comment="QR Code description">Quét mã này để tải bàn phím này \n trên thiết bị khác</string>
|
||||
<!-- Context: Keyboard Help welcome.htm title -->
|
||||
<string name="welcome_package" comment="Title to welcome.htm page (name and version)">Chào mừng đến với %1$s</string>
|
||||
<!-- Context: Keyboard app doesn't include FileProvider library needed to view help file -->
|
||||
<string name="fileprovider_undefined" comment="FileProvider library needed to view help file">Cần có Thư viện FileProvider để xem phần trợ giúp file: %1$s</string>
|
||||
<!-- Context: Fatal keyboard error. Will load default keyboard -->
|
||||
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard">Lỗi bàn phím nghiêm trọng với %1$s: %2$s cho ngôn ngữ %3$s. Đang tải bàn phím mặc định.</string>
|
||||
<!-- Context: Fatal keyboard error. -->
|
||||
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language).">Lỗi trong bàn phím %1$s:%2$s cho ngôn ngữ %3$s.</string>
|
||||
<!-- Context: Query for associated dictionary -->
|
||||
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Kiểm tra từ điển liên quan để tải về</string>
|
||||
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download">Không thể kết nối với server của Keyman để kiểm tra từ điển được liên kết để tải xuống</string>
|
||||
<!-- Context: Model Updates -->
|
||||
<string name="confirm_download_model" comment="Confirmation to download a dictionary update">Bạn có muốn tải xuống phiên bản mới nhất của từ điển này?</string>
|
||||
<!-- Context: No associated dictionary found -->
|
||||
<string name="no_associated_model" comment="Confirmation there's no available dictionary to download">Không có từ điển để tải xuống</string>
|
||||
<!-- Context: Model Updates -->
|
||||
<string name="catalog_unavailable" comment="Notification that the cloud resource catalog can't be updated">Catalog kho dữ liệu hiện không có sẵn</string>
|
||||
<!-- Context: Background download messages-->
|
||||
<string name="catalog_download_start_in_background" comment="Notification that the resource catalog update will begin">Cập nhật catalog đã được bắt đầu ngầm</string>
|
||||
<!-- Context: Background download messages-->
|
||||
<string name="catalog_download_is_running_in_background" comment="Notification that the resource catalog update is still downloading">Catalog vẫn đang tải về; xin vui lòng thử lại sau một lát!</string>
|
||||
<!-- Context: Background download messages-->
|
||||
<string name="progress_message_checking_resource_is_running" comment="Progress notification that the check for a resource is still running ">Đang kiểm tra kho dữ liệu</string>
|
||||
<!-- Context: Background download messages-->
|
||||
<string name="keyboard_download_start_in_background" comment="Notification that a keyboard download has started">Bàn phím đã bắt đầu tải về ngầm</string>
|
||||
<!-- Context: Background download messages-->
|
||||
<string name="keyboard_download_is_running_in_background" comment="Notification that a keyboard download is still running">Bàn phím được chọn đang được tải về; vui lòng thử lại sau một lát!</string>
|
||||
<!-- Context: Background download messages-->
|
||||
<string name="keyboard_download_finished" comment="Notification that a keyboard download has finished">Bàn phím đã được tải xong!</string>
|
||||
<!-- Context: Background download messages-->
|
||||
<string name="dictionary_download_start_in_background" comment="Notification that a dictionary download has started">Từ điển đã bắt đầu tải về ngầm</string>
|
||||
<!-- Context: Background download messages-->
|
||||
<string name="dictionary_download_is_running_in_background" comment="Notification that a dictionary download is still running">Từ điển được chọn đang được tải về; vui lòng thử lại sau một lát!</string>
|
||||
<!-- Context: Background download messages. Removed in Keyman 17-->
|
||||
<string name="dictionary_download_finished" comment="Notification that a dictionary download has finished">Tải từ điển hoàn tất.</string>
|
||||
<!-- Context: Background download messages -->
|
||||
<string name="download_failed" comment="Notification that a download failed">Tải xuống thất bại</string>
|
||||
<!-- Context: Background download messages -->
|
||||
<string name="failed_to_retrieve_file" comment="Failed to retrieve downloaded file">Không thể thu hồi file đã tải về</string>
|
||||
<!-- Context: General Updates -->
|
||||
<string name="update_check_unavailable" comment="Error message when a Keyman server can't be reached">Không thể kết nối với máy chủ!</string>
|
||||
<!-- Context: General Updates -->
|
||||
<string name="update_check_current" comment="Notification that all resources are up to date">"Toàn bộ kho dữ liệu đã được cập nhật!"</string>
|
||||
<!-- Context: General Updates -->
|
||||
<string name="update_failed" comment="Notification that a resource update failed">Một hay nhiều dữ liệu không thể cập nhật!</string>
|
||||
<!-- Context: General Updates. Removed in Keyman 17 -->
|
||||
<string name="update_success" comment="Notification that a resource update successfully updated">Kho dữ liệu đã được cập nhật thành công!</string>
|
||||
<!-- Context: Model Info -->
|
||||
<string name="model_version" comment="Title for a dictionary version">Phiên bản từ điển</string>
|
||||
<!-- Context: Model Info -->
|
||||
<string name="uninstall_model" comment="Label to uninstall a dictionary">Gỡ cài đặt từ điển</string>
|
||||
<!-- Context: Model Info -->
|
||||
<string name="confirm_delete_model" comment="Confirmation to delete a dictionary">Bạn có muốn xóa từ điển này không?</string>
|
||||
<!-- Context: Model Deleted -->
|
||||
<string name="model_deleted" comment="Notification when a dictionary is deleted">Từ điển đã bị xóa</string>
|
||||
<!-- Context: Language Settings Keyboard install strings -->
|
||||
<string name="keyboard_install_toast" comment="Notification when a keyboard is installed">%1$s bàn phím đã được cài đặt</string>
|
||||
<!-- Context: Language Settings Keyboard uninstall strings -->
|
||||
<string name="keyboard_deleted_toast" comment="Notification when a keyboard is deleted">Bàn phím đã bị xóa</string>
|
||||
<!-- Context: Language Settings menu strings. Removed in Keyman 18 -->
|
||||
<string name="enable_corrections" comment="Enable corrections from the suggestion banner">Bật hiệu chỉnh</string>
|
||||
<!-- Context: Language Settings menu strings. Removed in Keyman 18 -->
|
||||
<string name="enable_predictions" comment="Enable predictions from the suggestion banner">Bật tiên đoán</string>
|
||||
<!-- Context: Language Settings menu strings - radio buttons -->
|
||||
<string name="suggestions_radio_0" comment="Predictions and corrections disabled">Tắt các đề xuất</string>
|
||||
<string name="suggestions_radio_1" comment="Suggestions Enabled: Predictions with no corrections">Chỉ dự đoán</string>
|
||||
<string name="suggestions_radio_2" comment="Suggestions Enabled: Predictions with corrections">Dự đoán có sửa chữa</string>
|
||||
<string name="suggestions_radio_3" comment="Suggestions Enabled: Predictions with auto-corrections">Dự đoán có sửa chữa tự động</string>
|
||||
<!-- Context: Language Settings menu strings -->
|
||||
<string name="model_label">Từ điển</string>
|
||||
<plurals name="model_count">
|
||||
<item quantity="other">Từ điển</item>
|
||||
</plurals>
|
||||
<!-- Context: Language Settings menu substring - Check for available dictionary -->
|
||||
<string name="check_available_model" comment="Check for available dictionary">Kiểm tra từ điển có sẵn</string>
|
||||
<string name="check_model_online" comment="Check for dictionaries online">Kiểm tra từ điển trên mạng</string>
|
||||
<!-- Context: Language Settings menu strings -->
|
||||
<string name="model_info_header" comment="Dictionary name">Từ điển: %1$s</string>
|
||||
<!-- Context: Language Settings menu strings -->
|
||||
<string name="model_picker_header" comment="Dictionaries for a specified language">%1$s Từ điển</string>
|
||||
<!-- Context: Language Settings menu strings -->
|
||||
<!-- Context: Language Settings menu strings -->
|
||||
<string name="model_install_toast" comment="Notification when a dictionary is installed">Từ điển đã được cài đặt</string>
|
||||
<!-- Context: Language Settings menu strings -->
|
||||
<plurals name="keyboard_count">
|
||||
<item quantity="other">(%1$d bàn phím)</item>
|
||||
</plurals>
|
||||
<!-- Context: "Change Display Language" menu -->
|
||||
<string name="default_locale" comment="Use the device's current locale">Vị trí mặc định</string>
|
||||
<!-- Context: Content descriptions -->
|
||||
<!-- Context: Content descriptions -->
|
||||
<!-- Context: Popup menu labels -->
|
||||
<string name="label_delete" comment="Confirmation to delete an item">Xóa</string>
|
||||
<!-- Context: Shared preferences name -->
|
||||
<!-- Context: Other strings -->
|
||||
<string name="help_bubble_text">Chạm ở đây để đổi bàn phím</string>
|
||||
<!-- Context: anywhere -->
|
||||
<string name="unable_to_open_browser" comment="Notification when a browser activity cannot be launched">Không thể mở trình duyệt</string>
|
||||
</resources>
|
||||
|
|
@ -9,14 +9,14 @@
|
|||
|
||||
<!-- Context: Title for list -->
|
||||
<plurals name="title_keyboards">
|
||||
<item quantity="one">Keyboard</item>
|
||||
<item quantity="other">Keyboards</item>
|
||||
<item quantity="one">Keyboard</item>
|
||||
<item quantity="other">Keyboards</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Context: Title for list -->
|
||||
<plurals name="title_other_input_methods">
|
||||
<item quantity="one">Other Input Method</item>
|
||||
<item quantity="other">Other Input Methods</item>
|
||||
<item quantity="one">Other Input Method</item>
|
||||
<item quantity="other">Other Input Methods</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Context: Title for list -->
|
||||
|
|
@ -127,10 +127,24 @@
|
|||
<string name="fatal_keyboard_error" comment="Fatal keyboard error (keyboard ID::packageID for language). Will load default keyboard">
|
||||
Fatal keyboard error with %1$s:%2$s for %3$s language. Loading default keyboard.</string>
|
||||
|
||||
<!-- Context: Fatal keyboard error. -->
|
||||
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language).">
|
||||
<!-- Context: Fatal keyboard error. -->
|
||||
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language).">
|
||||
Error in keyboard %1$s:%2$s for %3$s language.</string>
|
||||
|
||||
<!-- Context: Alert Dialog body: System WebView not installed -->
|
||||
<string name="body_install_webview" comment="Notification when WebView not installed">
|
||||
Keyman requires WebView to be installed."</string>
|
||||
|
||||
<!-- Context: Alert Dialog body: System WebView installed but not enabled -->
|
||||
<string name="body_enable_webview" comment="Notification when WebView installed but not enabled">
|
||||
Keyman requires WebView to be enabled."</string>
|
||||
|
||||
<!-- Context Button label: Install WebView -->
|
||||
<string name="label_install_webview" comment="Button label to install webview">Install WebView</string>
|
||||
|
||||
<!-- Context Button label: Enable WebView -->
|
||||
<string name="label_enable_webview" comment="Button label to enable webview">Enable WebView</string>
|
||||
|
||||
<!-- Context: Query for associated dictionary -->
|
||||
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Checking for associated dictionary to download</string>
|
||||
|
||||
|
|
@ -184,6 +198,9 @@
|
|||
<!-- Context: General Updates -->
|
||||
<string name="update_check_unavailable" comment="Error message when a Keyman server can't be reached">Failed to access server!</string>
|
||||
|
||||
<!-- Context: Update-check failure notification -->
|
||||
<string name="update_check_download_manager_disabled" comment="Notification that a system service needed for updates is disabled">DownloadManager disabled - cannot check for updates</string>
|
||||
|
||||
<!-- Context: General Updates -->
|
||||
<string name="update_check_current" comment="Notification that all resources are up to date">"All resources are up to date!"</string>
|
||||
|
||||
|
|
@ -229,8 +246,8 @@
|
|||
<!-- Context: Language Settings menu strings -->
|
||||
<string name="model_label">Dictionary</string>
|
||||
<plurals name="model_count">
|
||||
<item quantity="one">Dictionary</item>
|
||||
<item quantity="other">Dictionaries</item>
|
||||
<item quantity="one">Dictionary</item>
|
||||
<item quantity="other">Dictionaries</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Context: Language Settings menu substring - Check for available dictionary -->
|
||||
|
|
@ -251,8 +268,8 @@
|
|||
|
||||
<!-- Context: Language Settings menu strings -->
|
||||
<plurals name="keyboard_count">
|
||||
<item quantity="one">(%1$d keyboard)</item>
|
||||
<item quantity="other">(%1$d keyboards)</item>
|
||||
<item quantity="one">(%1$d keyboard)</item>
|
||||
<item quantity="other">(%1$d keyboards)</item>
|
||||
</plurals>
|
||||
|
||||
|
||||
|
|
@ -278,4 +295,5 @@
|
|||
|
||||
<!-- Context: anywhere -->
|
||||
<string name="unable_to_open_browser" comment="Notification when a browser activity cannot be launched">Unable to launch web browser</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@
|
|||
<item name="android:textColorPrimary">@android:color/black</item>
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
</style>
|
||||
<style name="AppTheme.NoTitleDialog" parent="AppTheme.Light.Dialog">
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Dialog.Alert" parent="Theme.AppCompat.Light.Dialog.Alert">
|
||||
<item name="android:textColor">@android:color/black</item>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
title: KMManager.closeParentAppOnShowKeyboardPicker()
|
||||
---
|
||||
|
||||
## Summary
|
||||
The **closeParentAppOnShowKeyboardPicker()** method restores the default behavior of the keyboard picker menu closing the parent app.
|
||||
|
||||
## Syntax
|
||||
|
||||
```java
|
||||
KMManager.closeParentAppOnShowKeyboardPicker()
|
||||
```
|
||||
|
||||
## Description
|
||||
Normally, launching the KeyboardPickerActivity from a system keyboard closes the parent app.
|
||||
|
||||
(The keyboard picker becomes the root of the activity stack.)
|
||||
|
||||
Calling this function restores this default behavior.
|
||||
|
||||
## Examples
|
||||
|
||||
### Example: Using `closeParentAppOnShowKeyboardPicker()`
|
||||
|
||||
The following script illustrates the use of `closeParentAppOnShowKeyboardPicker()`:
|
||||
```java
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
KMManager.onResume();
|
||||
KMManager.hideSystemKeyboard();
|
||||
|
||||
// Reset keyboard picker Activity Task flag
|
||||
KMManager.closeParentAppOnShowKeyboardPicker();
|
||||
```
|
||||
|
||||
## See also
|
||||
* [dontCloseParentAppOnShowKeyboardPicker()](dontCloseParentAppOnShowKeyboardPicker)
|
||||
* [showKeyboardPicker()](showKeyboardPicker)
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
---
|
||||
title: KMManager.dontCloseParentAppOnShowKeyboardPicker()
|
||||
---
|
||||
|
||||
## Summary
|
||||
The **dontCloseParentAppOnShowKeyboardPicker()** method disables the keyboard picker task flag so keyboard picker doesn't dismiss the parent app.
|
||||
|
||||
## Syntax
|
||||
|
||||
```javascript
|
||||
KMManager.dontCloseParentAppOnShowKeyboardPicker()
|
||||
```
|
||||
|
||||
## Description
|
||||
Normally, launching the KeyboardPickerActivity from a system keyboard closes the parent app.
|
||||
|
||||
(The keyboard picker becomes the root of the activity stack)
|
||||
|
||||
Calling this function keeps the parent app running in the background.
|
||||
|
||||
## Examples
|
||||
|
||||
### Example: Using `dontCloseParentAppOnShowKeyboardPicker()`
|
||||
|
||||
The following script illustrates the use of `dontCloseParentAppOnShowKeyboardPicker()`:
|
||||
```java
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// When using Keyman system keyboard within the Keyman app,
|
||||
// disable keyboard picker task flag so keyboard picker doesn't dismiss Keyman app
|
||||
KMManager.dontCloseParentAppOnShowKeyboardPicker();
|
||||
```
|
||||
|
||||
## See also
|
||||
* [closeParentAppOnShowKeyboardPicker()](closeParentAppOnShowKeyboardPicker)
|
||||
* [showKeyboardPicker()](showKeyboardPicker)
|
||||
|
|
@ -34,5 +34,4 @@ The following code illustrates the use of `getLongpressDelay()`:
|
|||
Keyman Engine for Android 18.0: New function.
|
||||
|
||||
## See also
|
||||
* [sendOptionsToKeyboard](sendOptionsToKeyboard)
|
||||
* [setLongpressDelay](setLongpressDelay)
|
||||
|
|
|
|||
|
|
@ -50,6 +50,9 @@ The KMManager is the core class which provides most of the methods and constants
|
|||
[`canRemoveKeyboard()`](canRemoveKeyboard)
|
||||
: returns whether removing a keyboard is enabled, like in the keyboard picker menu
|
||||
|
||||
[`closeParentAppOnShowKeyboardPicker()`](closeParentAppOnShowKeyboardPicker)
|
||||
: restores keyboard picker task flag so keyboard picker dismisses the parent app
|
||||
|
||||
[`copyHTMLBannerAssets()`](copyHTMLBannerAssets)
|
||||
: copies a folder of HTML banner assets so it's available for your keyboard app's resources
|
||||
|
||||
|
|
@ -59,6 +62,9 @@ The KMManager is the core class which provides most of the methods and constants
|
|||
[`deregisterLexicalModel()`](deregisterLexicalModel)
|
||||
: deregisters the specified lexical model from the LMLayer so it isn't used
|
||||
|
||||
[`dontCloseParentAppOnShowKeyboardPicker()`](dontCloseParentAppOnShowKeyboardPicker)
|
||||
: disables keyboard picker task flag so keyboard picker doesn't dismiss the parent app
|
||||
|
||||
[`executeHardwareKeystroke()`](executeHardwareKeystroke)
|
||||
: process the keystroke generated from a physical keyboard
|
||||
|
||||
|
|
@ -212,9 +218,6 @@ The KMManager is the core class which provides most of the methods and constants
|
|||
[`removeKeyboardEventListener()`](removeKeyboardEventListener)
|
||||
: removes the specified listener from the list of keyboard event listeners
|
||||
|
||||
[`sendOptionsToKeyboard()`](sendOptionsToKeyboard)
|
||||
: sends options like longpress delay to the KeymanWeb keyboard
|
||||
|
||||
[`setCanAddNewKeyboard()`](setCanAddNewKeyboard)
|
||||
: sets whether adding a new keyboard is allowed
|
||||
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
---
|
||||
title: KMManager.sendOptionsToKeyboard()
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
The `sendOptionsToKeyboard()` method sends options like longpress delay to the KeymanWeb keyboard.
|
||||
|
||||
## Syntax
|
||||
|
||||
```java
|
||||
KMManager.sendOptionsToKeyboard()
|
||||
```
|
||||
|
||||
## Description
|
||||
Use this method to update options in the KeymanWeb keyboard.
|
||||
* Number of milliseconds to trigger a longpress gesture
|
||||
|
||||
This method requires a keyboard to be loaded for the values to take effect.
|
||||
|
||||
## Examples
|
||||
|
||||
### Example: Using `sendOptionsToKeyboard()`
|
||||
|
||||
The following code illustrates the use of `sendOptionsToKeyboard()`:
|
||||
```java
|
||||
int currentDelayTimeMS = 250;
|
||||
|
||||
// Store currentDelayTimeMS
|
||||
KMManager.setLongpressDelay(currentDelayTimeMS);
|
||||
|
||||
// Apply the keyboard options
|
||||
KMManager.sendOptionsToKeyboard();
|
||||
```
|
||||
|
||||
## History
|
||||
Keyman Engine for Android 18.0: New function.
|
||||
|
||||
## See also
|
||||
* [getLongpressDelay](getLongpressDelay)
|
||||
* [setLongpressDelay](setLongpressDelay)
|
||||
|
|
@ -4,7 +4,7 @@ title: KMManager.setLongpressDelay()
|
|||
|
||||
## Summary
|
||||
|
||||
The `setLongpressDelay()` method sets the number of milliseconds to trigger a longpress gesture as a stored preference.
|
||||
The `setLongpressDelay()` method sets the number of milliseconds to trigger a longpress gesture, applies to the keyboard, and stores preference.
|
||||
|
||||
## Syntax
|
||||
|
||||
|
|
@ -27,11 +27,8 @@ The following code illustrates the use of `setLongpressDelay()`:
|
|||
```java
|
||||
int currentDelayTimeMS = 300;
|
||||
|
||||
// Store currentDelayTimeMS
|
||||
// Store currentDelayTimeMS and applies
|
||||
KMManager.setLongpressDelay(currentDelayTimeMS);
|
||||
|
||||
// Apply the keyboard options
|
||||
KMManager.sendOptionsToKeyboard();
|
||||
```
|
||||
|
||||
## History
|
||||
|
|
@ -39,4 +36,3 @@ Keyman Engine for Android 18.0: New function.
|
|||
|
||||
## See also
|
||||
* [getLongpressDelay](getLongpressDelay)
|
||||
* [sendOptionsToKeyboard](sendOptionsToKeyboard)
|
||||
|
|
|
|||
BIN
common/resources/bitmaps/windows/kmshell/FullSizeCtrlK.png
Normal file
BIN
common/resources/bitmaps/windows/kmshell/FullSizeCtrlK.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 86 KiB |
BIN
common/resources/bitmaps/windows/kmshell/FullSizeKeyboard.png
Normal file
BIN
common/resources/bitmaps/windows/kmshell/FullSizeKeyboard.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 116 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 88 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 101 KiB |
BIN
common/resources/bitmaps/windows/kmshell/FullSizeUpdate.png
Normal file
BIN
common/resources/bitmaps/windows/kmshell/FullSizeUpdate.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 138 KiB |
BIN
common/resources/bitmaps/windows/kmshell/FullSizeWrench.png
Normal file
BIN
common/resources/bitmaps/windows/kmshell/FullSizeWrench.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 109 KiB |
Binary file not shown.
1
common/test/keyboards/baseline/.gitignore
vendored
Normal file
1
common/test/keyboards/baseline/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
kmcomp/
|
||||
36
common/test/keyboards/baseline/build.sh
Executable file
36
common/test/keyboards/baseline/build.sh
Executable file
|
|
@ -0,0 +1,36 @@
|
|||
#!/usr/bin/env bash
|
||||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "${THIS_SCRIPT%/*}/../../../../resources/build/builder.inc.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
builder_describe "Baseline keyboards tests -- built with 16.0 compiler" \
|
||||
clean \
|
||||
configure \
|
||||
build
|
||||
|
||||
builder_describe_platform \
|
||||
:project win
|
||||
|
||||
builder_parse "$@"
|
||||
|
||||
builder_describe_outputs \
|
||||
configure kmcomp/kmcomp.exe \
|
||||
build k_000___null_keyboard.kmx
|
||||
|
||||
do_configure() {
|
||||
mkdir -p kmcomp
|
||||
unzip kmcomp-16.0.138.zip -d kmcomp/
|
||||
}
|
||||
|
||||
do_build() {
|
||||
local name
|
||||
for name in *.kmn; do
|
||||
./kmcomp/kmcomp.exe -no-compiler-version -d "$name"
|
||||
done
|
||||
}
|
||||
|
||||
builder_run_action clean rm -rf *.kmx ./kmcomp/
|
||||
builder_run_action configure do_configure
|
||||
builder_run_action build do_build
|
||||
15
common/test/keyboards/baseline/k_050___nul_and_context.kmn
Normal file
15
common/test/keyboards/baseline/k_050___nul_and_context.kmn
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
store(&NAME) '050 - nul_and_context'
|
||||
c Description: Tests that context has the right offset when used with nul
|
||||
c keys: [K_T][K_B]
|
||||
c expected: 2t3
|
||||
c context:
|
||||
|
||||
store(&version) '10.0'
|
||||
|
||||
begin Unicode > use(Main)
|
||||
|
||||
store(cons) 'mnpqrstv'
|
||||
|
||||
group(Main) using keys
|
||||
|
||||
nul any(cons) + 'b' > '2' context(2) '3'
|
||||
BIN
common/test/keyboards/baseline/k_050___nul_and_context.kmx
Normal file
BIN
common/test/keyboards/baseline/k_050___nul_and_context.kmx
Normal file
Binary file not shown.
16
common/test/keyboards/baseline/k_051___if_and_context.kmn
Normal file
16
common/test/keyboards/baseline/k_051___if_and_context.kmn
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
store(&NAME) '051 - if_and_context'
|
||||
c Description: Tests that context has the right offset when used with if
|
||||
c keys: [K_T][K_D]
|
||||
c expected: 4t5
|
||||
c context:
|
||||
|
||||
store(&version) '10.0'
|
||||
|
||||
begin Unicode > use(Main)
|
||||
|
||||
store(cons) 'mnpqrstv'
|
||||
store(ifx) '1'
|
||||
|
||||
group(Main) using keys
|
||||
|
||||
if(ifx = '1') any(cons) + 'd' > '4' context(2) '5'
|
||||
BIN
common/test/keyboards/baseline/k_051___if_and_context.kmx
Normal file
BIN
common/test/keyboards/baseline/k_051___if_and_context.kmx
Normal file
Binary file not shown.
16
common/test/keyboards/baseline/k_052___nul_and_index.kmn
Normal file
16
common/test/keyboards/baseline/k_052___nul_and_index.kmn
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
store(&NAME) '052 - nul_and_index'
|
||||
c Description: Tests that context has the right offset when used with nul
|
||||
c keys: [K_T][K_B]
|
||||
c expected: 2T3
|
||||
c context:
|
||||
|
||||
store(&version) '10.0'
|
||||
|
||||
begin Unicode > use(Main)
|
||||
|
||||
store(cons) 'mnpqrstv'
|
||||
store(outs) 'MNPQRSTV'
|
||||
|
||||
group(Main) using keys
|
||||
|
||||
nul any(cons) + 'b' > '2' index(outs, 2) '3'
|
||||
BIN
common/test/keyboards/baseline/k_052___nul_and_index.kmx
Normal file
BIN
common/test/keyboards/baseline/k_052___nul_and_index.kmx
Normal file
Binary file not shown.
18
common/test/keyboards/baseline/k_053___if_and_index.kmn
Normal file
18
common/test/keyboards/baseline/k_053___if_and_index.kmn
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
store(&NAME) '053 - if_and_index'
|
||||
c Description: Tests that index() has the right offset when used with if
|
||||
c keys: [K_T][K_D]
|
||||
c expected: 4T5
|
||||
c context:
|
||||
|
||||
store(&version) '10.0'
|
||||
|
||||
begin Unicode > use(Main)
|
||||
|
||||
store(cons) 'mnpqrstv'
|
||||
store(outs) 'MNPQRSTV'
|
||||
store(ifx) '1'
|
||||
|
||||
group(Main) using keys
|
||||
|
||||
if(ifx = '1') any(cons) + 'd' > '4' index(outs, 2) '5'
|
||||
|
||||
BIN
common/test/keyboards/baseline/k_053___if_and_index.kmx
Normal file
BIN
common/test/keyboards/baseline/k_053___if_and_index.kmx
Normal file
Binary file not shown.
15
common/test/keyboards/baseline/k_054___nul_and_contextex.kmn
Normal file
15
common/test/keyboards/baseline/k_054___nul_and_contextex.kmn
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
store(&NAME) '054 - nul_and_contextex'
|
||||
c Description: Tests that context in context part of rule has the right offset when used with nul
|
||||
c keys: [K_T][K_T][K_B]
|
||||
c expected: 2tt3
|
||||
c context:
|
||||
|
||||
store(&version) '10.0'
|
||||
|
||||
begin Unicode > use(Main)
|
||||
|
||||
store(cons) 'mnpqrstv'
|
||||
|
||||
group(Main) using keys
|
||||
|
||||
nul any(cons) context(2) + 'b' > '2' context(2) context(3) '3'
|
||||
BIN
common/test/keyboards/baseline/k_054___nul_and_contextex.kmx
Normal file
BIN
common/test/keyboards/baseline/k_054___nul_and_contextex.kmx
Normal file
Binary file not shown.
BIN
common/test/keyboards/baseline/kmcomp-16.0.138.zip
Normal file
BIN
common/test/keyboards/baseline/kmcomp-16.0.138.zip
Normal file
Binary file not shown.
|
|
@ -1418,7 +1418,7 @@ var
|
|||
// but this requires a file format change, so a much bigger scope
|
||||
|
||||
if not Enabled then Font.Color := $808080
|
||||
else Font.Color := clWindowText;
|
||||
else Font.Color := KeyFontColor_Cap;
|
||||
|
||||
FKeyboard.FDrawChar.DisplayQuality := ctCleartype;
|
||||
FKeyboard.FDrawChar.Color := Font.Color;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
|||
. "${THIS_SCRIPT%/*}/../../../../resources/build/builder.inc.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
builder_describe "Common Delphi components" clean configure build test
|
||||
builder_describe "Common Delphi components" clean configure build test edit
|
||||
builder_parse "$@"
|
||||
|
||||
#-------------------------------------------------------------------------------------------------------------------
|
||||
|
|
@ -28,3 +28,4 @@ builder_run_action clean:project clean_windows_project_files
|
|||
builder_run_action configure:project configure_windows_build_environment
|
||||
builder_run_action build:project do_build
|
||||
# builder_run_action test:project do_test
|
||||
builder_run_action edit:project start common_components.dproj
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ type
|
|||
const S_CustomisationFilename = 'desktop_pro.pxx';
|
||||
|
||||
const S_KeymanAppData_UpdateCache = 'Keyman\UpdateCache\';
|
||||
|
||||
class var FHaveCachedKeymanRoot: Boolean;
|
||||
class var FCachedKeymanRoot: string;
|
||||
public
|
||||
const S_KMShell = 'kmshell.exe';
|
||||
const S_TSysInfoExe = 'tsysinfo.exe';
|
||||
|
|
@ -434,12 +437,21 @@ end;
|
|||
|
||||
class function TKeymanPaths.RunningFromSource(var keyman_root: string): Boolean;
|
||||
begin
|
||||
if FHaveCachedKeymanRoot then
|
||||
begin
|
||||
keyman_root := FCachedKeymanRoot;
|
||||
Exit(keyman_root <> '');
|
||||
end;
|
||||
// On developer machines, if we are running within the source repo, then use
|
||||
// those paths
|
||||
keyman_root := GetEnvironmentVariable('KEYMAN_ROOT');
|
||||
keyman_root := GetEnvironmentVariable('KEYMAN_ROOT').Replace('/', '\', [rfReplaceAll]);
|
||||
if keyman_root <> '' then
|
||||
keyman_root := IncludeTrailingPathDelimiter(keyman_root);
|
||||
Result := (keyman_root <> '') and SameText(keyman_root, ParamStr(0).Substring(0, keyman_root.Length));
|
||||
|
||||
FHaveCachedKeymanRoot := True;
|
||||
if Result then
|
||||
FCachedKeymanRoot := keyman_root;
|
||||
end;
|
||||
|
||||
class function TKeymanPaths.KeymanCoreLibraryPath(const Filename: string): string;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
|||
. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
builder_describe "Build build_standards_data tool" clean configure build test
|
||||
builder_describe "Build build_standards_data tool" clean configure build test edit
|
||||
builder_parse "$@"
|
||||
|
||||
#-------------------------------------------------------------------------------------------------------------------
|
||||
|
|
@ -49,3 +49,4 @@ builder_run_action clean:project clean_windows_project_files
|
|||
builder_run_action configure:project configure_windows_build_environment
|
||||
builder_run_action build:project do_build
|
||||
# builder_run_action test:project do_test
|
||||
builder_run_action edit:project start build_standards_data.dproj
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
|||
|
||||
builder_describe "Unicode character database build tool" \
|
||||
@/common/windows/delphi:keymanversion \
|
||||
clean configure build test
|
||||
clean configure build test edit
|
||||
builder_parse "$@"
|
||||
|
||||
#-------------------------------------------------------------------------------------------------------------------
|
||||
|
|
@ -30,3 +30,4 @@ builder_run_action clean:project clean_windows_project_files
|
|||
builder_run_action configure:project configure_windows_build_environment
|
||||
builder_run_action build:project do_build
|
||||
# builder_run_action test:project do_test
|
||||
builder_run_action edit:project start buildunidata.dproj
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
|||
builder_describe \
|
||||
"Development and build utilities for Delphi" \
|
||||
@/common/windows/delphi:keymanversion \
|
||||
clean configure build test prepublish
|
||||
clean configure build test prepublish edit
|
||||
|
||||
builder_parse "$@"
|
||||
|
||||
|
|
@ -30,3 +30,4 @@ builder_run_action configure:project configure_windows_build_environment
|
|||
builder_run_action build:project delphi_msbuild devtools.dproj "//p:Platform=Win32"
|
||||
# builder_run_action test:project do_test
|
||||
builder_run_action prepublish:project "$DEVTOOLS" -rt
|
||||
builder_run_action edit:project start devtools.dproj
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
|||
. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
builder_describe "Sentrytool for converting Delphi symbols into sentry-readable format" clean configure build test
|
||||
builder_describe "Sentrytool for converting Delphi symbols into sentry-readable format" \
|
||||
clean configure build test edit
|
||||
builder_parse "$@"
|
||||
|
||||
#-------------------------------------------------------------------------------------------------------------------
|
||||
|
|
@ -30,3 +31,4 @@ builder_run_action clean:project clean_windows_project_files
|
|||
builder_run_action configure:project configure_windows_build_environment
|
||||
builder_run_action build:project delphi_msbuild sentrytool.dproj "//p:Platform=Win32"
|
||||
# builder_run_action test:project do_test
|
||||
builder_run_action edit:project start sentrytool.dproj
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
|||
. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
builder_describe "Tool for validating klog is disabled for release builds" clean configure build test prepublish
|
||||
builder_describe "Tool for validating klog is disabled for release builds" \
|
||||
clean configure build test prepublish edit
|
||||
builder_parse "$@"
|
||||
|
||||
#-------------------------------------------------------------------------------------------------------------------
|
||||
|
|
@ -33,3 +34,4 @@ builder_run_action configure:project configure_windows_build_environment
|
|||
builder_run_action build:project do_build
|
||||
# builder_run_action test:project do_test
|
||||
builder_run_action prepublish:project "$WIN32_TARGET_PATH/test_klog.exe"
|
||||
builder_run_action edit:project start test_klog.dproj
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
|||
. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
builder_describe "Tool to verify all Windows executable signatures and manifests" clean configure build test verify
|
||||
builder_describe "Tool to verify all Windows executable signatures and manifests" \
|
||||
clean configure build test verify edit
|
||||
builder_parse "$@"
|
||||
|
||||
#-------------------------------------------------------------------------------------------------------------------
|
||||
|
|
@ -27,3 +28,4 @@ builder_run_action clean:project clean_windows_project_files
|
|||
builder_run_action configure:project configure_windows_build_environment
|
||||
builder_run_action build:project do_build
|
||||
# builder_run_action test:project do_test
|
||||
builder_run_action edit:project start verify_signatures.dproj
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ archtargets=(":wasm WASM build")
|
|||
case $BUILDER_OS in
|
||||
win)
|
||||
archtargets+=(
|
||||
":win Both x86 and x64"
|
||||
":x86 32-bit Windows (x86) build"
|
||||
":x64 64-bit Windows (x64) build"
|
||||
)
|
||||
|
|
@ -92,9 +93,12 @@ fi
|
|||
# generates a 'fat' library from them.
|
||||
builder_describe_internal_dependency \
|
||||
build:mac build:mac-x86_64 \
|
||||
build:mac build:mac-arm64
|
||||
build:mac build:mac-arm64 \
|
||||
build:win build:x86 \
|
||||
build:win build:x64
|
||||
|
||||
builder_describe_outputs \
|
||||
configure:win /core/build/win/$BUILDER_CONFIGURATION/ \
|
||||
configure:x86 /core/build/x86/$BUILDER_CONFIGURATION/build.ninja \
|
||||
configure:x64 /core/build/x64/$BUILDER_CONFIGURATION/build.ninja \
|
||||
configure:mac /core/build/mac/$BUILDER_CONFIGURATION/ \
|
||||
|
|
@ -102,6 +106,7 @@ builder_describe_outputs \
|
|||
configure:mac-arm64 /core/build/mac-arm64/$BUILDER_CONFIGURATION/build.ninja \
|
||||
configure:arch /core/build/arch/$BUILDER_CONFIGURATION/build.ninja \
|
||||
configure:wasm /core/build/wasm/$BUILDER_CONFIGURATION/build.ninja \
|
||||
build:win /core/build/win/$BUILDER_CONFIGURATION/BUILT \
|
||||
build:x86 /core/build/x86/$BUILDER_CONFIGURATION/src/libkeymancore.a \
|
||||
build:x64 /core/build/x64/$BUILDER_CONFIGURATION/src/libkeymancore.a \
|
||||
build:mac /core/build/mac/$BUILDER_CONFIGURATION/libkeymancore.a \
|
||||
|
|
@ -134,29 +139,28 @@ do_action() {
|
|||
# -------------------------------------------------------------------------------
|
||||
|
||||
do_action clean
|
||||
builder_run_action clean:mac rm -rf "$KEYMAN_ROOT/core/build/mac/$BUILDER_CONFIGURATION"
|
||||
builder_run_action clean:win rm -rf "$KEYMAN_ROOT/core/build/win/$BUILDER_CONFIGURATION"
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
|
||||
do_action configure
|
||||
|
||||
# After we have built the necessary internal dependencies, then we can go
|
||||
# ahead and build a fat library for external consumption
|
||||
if builder_start_action configure:mac; then
|
||||
mkdir -p "$KEYMAN_ROOT/core/build/mac/$BUILDER_CONFIGURATION"
|
||||
builder_finish_action success configure:mac
|
||||
fi
|
||||
# After we have built the necessary internal dependencies, then we can go ahead
|
||||
# and build a fat library for external consumption for mac, and no-op for win
|
||||
builder_run_action configure:mac mkdir -p "$KEYMAN_ROOT/core/build/mac/$BUILDER_CONFIGURATION"
|
||||
builder_run_action configure:win mkdir -p "$KEYMAN_ROOT/core/build/win/$BUILDER_CONFIGURATION"
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
|
||||
do_action build
|
||||
|
||||
if builder_start_action build:mac; then
|
||||
lipo -create \
|
||||
"$KEYMAN_ROOT/core/build/mac-x86_64/$BUILDER_CONFIGURATION/src/libkeymancore.a" \
|
||||
"$KEYMAN_ROOT/core/build/mac-arm64/$BUILDER_CONFIGURATION/src/libkeymancore.a" \
|
||||
-output "$KEYMAN_ROOT/core/build/mac/$BUILDER_CONFIGURATION/libkeymancore.a"
|
||||
builder_finish_action success build:mac
|
||||
fi
|
||||
builder_run_action build:mac lipo -create \
|
||||
"$KEYMAN_ROOT/core/build/mac-x86_64/$BUILDER_CONFIGURATION/src/libkeymancore.a" \
|
||||
"$KEYMAN_ROOT/core/build/mac-arm64/$BUILDER_CONFIGURATION/src/libkeymancore.a" \
|
||||
-output "$KEYMAN_ROOT/core/build/mac/$BUILDER_CONFIGURATION/libkeymancore.a"
|
||||
|
||||
builder_run_action build:win touch "$KEYMAN_ROOT/core/build/win/$BUILDER_CONFIGURATION/BUILT"
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
|
||||
|
|
@ -177,6 +181,15 @@ if builder_start_action test:mac; then
|
|||
builder_finish_action success test:mac
|
||||
fi
|
||||
|
||||
if builder_start_action test:win; then
|
||||
# We can assume that build:win has run so both architectures will be available
|
||||
MESON_PATH="$KEYMAN_ROOT/core/build/x86/$BUILDER_CONFIGURATION"
|
||||
meson test -C "$MESON_PATH" $testparams
|
||||
MESON_PATH="$KEYMAN_ROOT/core/build/x64/$BUILDER_CONFIGURATION"
|
||||
meson test -C "$MESON_PATH" $testparams
|
||||
builder_finish_action success test:win
|
||||
fi
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
|
||||
do_action install
|
||||
|
|
|
|||
|
|
@ -648,8 +648,8 @@ typedef KMX_DWORD KMXPLUS_USET;
|
|||
struct COMP_KMXPLUS_USET {
|
||||
static const KMX_DWORD IDENT = LDML_SECTIONID_USET;
|
||||
COMP_KMXPLUS_HEADER header;
|
||||
KMX_DWORD usetCount;
|
||||
KMX_DWORD rangeCount;
|
||||
KMX_DWORD_unaligned usetCount;
|
||||
KMX_DWORD_unaligned rangeCount;
|
||||
// see helper for: usets sub-table
|
||||
// see helper for: ranges sub-table
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ KMX_ProcessEvent::KMX_ProcessEvent() : m_actions(&m_context), m_options(&m_keybo
|
|||
m_indexStack = new KMX_WORD[GLOBAL_ContextStackSize];
|
||||
m_miniContext = new KMX_WCHAR[GLOBAL_ContextStackSize];
|
||||
m_miniContextIfLen = 0;
|
||||
m_miniContextStartsWithNul = FALSE;
|
||||
m_debug_items = nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -338,10 +339,14 @@ KMX_BOOL KMX_ProcessEvent::ProcessGroup(LPGROUP gp, KMX_BOOL *pOutputKeystroke)
|
|||
m_miniContextIfLen = xstrlen(kkp->dpContext) - xstrlen_ignoreifopt(kkp->dpContext);
|
||||
|
||||
// 11 Aug 2003 - I25(v6) - mcdurdin - CODE_NUL context support
|
||||
if(*kkp->dpContext == UC_SENTINEL && *(kkp->dpContext+1) == CODE_NUL)
|
||||
if(*kkp->dpContext == UC_SENTINEL && *(kkp->dpContext+1) == CODE_NUL) {
|
||||
m_miniContextStartsWithNul = TRUE;
|
||||
u16cpy(m_miniContext, /*GLOBAL_ContextStackSize,*/ m_context.Buf(xstrlen_ignoreifopt(kkp->dpContext)-1) /*, GLOBAL_ContextStackSize*/); // I3162 // I3536
|
||||
else
|
||||
}
|
||||
else {
|
||||
m_miniContextStartsWithNul = FALSE;
|
||||
u16cpy(m_miniContext, /*GLOBAL_ContextStackSize,*/ m_context.Buf(xstrlen_ignoreifopt(kkp->dpContext)) /*, GLOBAL_ContextStackSize*/); // I3162 // I3536
|
||||
}
|
||||
|
||||
m_miniContext[GLOBAL_ContextStackSize-1] = 0;
|
||||
|
||||
|
|
@ -458,7 +463,12 @@ int KMX_ProcessEvent::PostString(PKMX_WCHAR str, LPKEYBOARD lpkb, PKMX_WCHAR end
|
|||
}
|
||||
case CODE_CONTEXTEX:
|
||||
p++;
|
||||
for (q = m_miniContext, i = m_miniContextIfLen; *q && i < *p - 1; i++, q = incxstr(q));
|
||||
// We have to exclude nul and if() statements from our offset calculation
|
||||
i = m_miniContextIfLen;
|
||||
if(m_miniContextStartsWithNul) {
|
||||
i++;
|
||||
}
|
||||
for (q = m_miniContext; *q && i < *p - 1; i++, q = incxstr(q));
|
||||
if (*q) {
|
||||
KMX_BOOL ignoreOutputKeystroke;
|
||||
temp = incxstr(q);
|
||||
|
|
@ -600,6 +610,7 @@ KMX_BOOL KMX_ProcessEvent::ContextMatch(LPKEY kkp)
|
|||
memset(m_indexStack, 0, GLOBAL_ContextStackSize*sizeof(KMX_WORD));
|
||||
|
||||
p = kkp->dpContext;
|
||||
indexp = m_indexStack;
|
||||
|
||||
if(*p == 0)
|
||||
{
|
||||
|
|
@ -611,6 +622,7 @@ KMX_BOOL KMX_ProcessEvent::ContextMatch(LPKEY kkp)
|
|||
// If context buf is longer than the context, then obviously not start of doc.
|
||||
if(m_context.Buf(xstrlen_ignoreifopt(p))) return FALSE;
|
||||
p = incxstr(p);
|
||||
indexp++;
|
||||
if(*p == 0) return TRUE;
|
||||
}
|
||||
|
||||
|
|
@ -660,7 +672,6 @@ KMX_BOOL KMX_ProcessEvent::ContextMatch(LPKEY kkp)
|
|||
{
|
||||
return FALSE; // context buf is too short!
|
||||
}
|
||||
indexp = m_indexStack;
|
||||
|
||||
for(; *p && *q; p = incxstr(p))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -38,7 +38,10 @@ class KMX_ProcessEvent {
|
|||
private:
|
||||
PKMX_WORD m_indexStack;
|
||||
PKMX_WCHAR m_miniContext;
|
||||
int m_miniContextIfLen; // number of if() statements excluded from start of m_miniContext
|
||||
/** number of `if()` statements excluded from start of `m_miniContext` for offset calculations */
|
||||
int m_miniContextIfLen;
|
||||
/** flag if we need to account for `nul` in offset calculations */
|
||||
bool m_miniContextStartsWithNul;
|
||||
KMSTATE m_state;
|
||||
km_core_state *m_core_state;
|
||||
|
||||
|
|
|
|||
|
|
@ -80,7 +80,12 @@ tests = [
|
|||
'k_046___deadkey_and_contextex',
|
||||
'k_047___caps_always_off_initially_on',
|
||||
'k_048___modifier_keys_keep_context',
|
||||
'k_049___enter_invalidates_context'
|
||||
'k_049___enter_invalidates_context',
|
||||
'k_050___nul_and_context',
|
||||
'k_051___if_and_context',
|
||||
'k_052___nul_and_index',
|
||||
'k_053___if_and_index',
|
||||
'k_054___nul_and_contextex',
|
||||
]
|
||||
|
||||
node = find_program('node', required: true)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
|||
builder_describe \
|
||||
"Keyman Developer Delphi components" \
|
||||
@/common/windows/delphi \
|
||||
clean configure build test
|
||||
clean configure build test edit
|
||||
builder_parse "$@"
|
||||
|
||||
#-------------------------------------------------------------------------------------------------------------------
|
||||
|
|
@ -33,3 +33,4 @@ builder_run_action clean:project clean_windows_project_files
|
|||
builder_run_action configure:project configure_windows_build_environment
|
||||
builder_run_action build:project do_build
|
||||
# builder_run_action test:project do_test
|
||||
builder_run_action edit:project start delphi_components.dproj
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ type
|
|||
procedure WndProc(var Message: TMessage); override;
|
||||
public
|
||||
constructor Create(AOwner: TComponent); override;
|
||||
function GetTextCR: string;
|
||||
function GetSelTextCR: string;
|
||||
published
|
||||
property AllowUnicode: Boolean read FAllowUnicodeInput write SetAllowUnicode default True;
|
||||
property OnMessage: TKeymanDeveloperDebuggerMessageEvent read FOnMessage write FOnMessage;
|
||||
|
|
@ -65,6 +67,9 @@ procedure Register;
|
|||
|
||||
implementation
|
||||
|
||||
uses
|
||||
System.SysUtils;
|
||||
|
||||
{ TKeymanDeveloperDebuggerMemo }
|
||||
|
||||
procedure TKeymanDeveloperDebuggerMemo.CNKeyDown(var Message: TWMKeyDown);
|
||||
|
|
@ -123,6 +128,16 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
function TKeymanDeveloperDebuggerMemo.GetSelTextCR: string;
|
||||
begin
|
||||
Result := SelText.Replace(#$0D#$0A, #$0D, [rfReplaceAll]);
|
||||
end;
|
||||
|
||||
function TKeymanDeveloperDebuggerMemo.GetTextCR: string;
|
||||
begin
|
||||
Result := string(Text).Replace(#$0D#$0A, #$0D, [rfReplaceAll]);
|
||||
end;
|
||||
|
||||
procedure TKeymanDeveloperDebuggerMemo.SetAllowUnicode(const Value: Boolean);
|
||||
begin
|
||||
if Value <> fAllowUnicodeInput then
|
||||
|
|
|
|||
|
|
@ -272,6 +272,9 @@ namespace KmnCompilerMessages {
|
|||
|
||||
HINT_IndexStoreLong = SevHint | 0x0B0,
|
||||
|
||||
ERROR_ContextExCannotReferenceIf = SevError | 0x0B1,
|
||||
ERROR_ContextExCannotReferenceNul = SevError | 0x0B2,
|
||||
|
||||
FATAL_BufferOverflow = SevFatal | 0x0C0
|
||||
// FATAL_Break = SevFatal | 0x0C1, unused
|
||||
};
|
||||
|
|
|
|||
|
|
@ -49,5 +49,5 @@ function do_build() {
|
|||
builder_run_action clean rm -rf ./build/
|
||||
builder_run_action configure verify_npm_setup
|
||||
builder_run_action build do_build
|
||||
builder_run_action test builder_do_typescript_tests 50
|
||||
builder_run_action test builder_do_typescript_tests 45
|
||||
builder_run_action publish builder_publish_npm
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import * as os from 'os';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'node:os';
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
|
||||
export interface KeymanDeveloperOptions {
|
||||
"use tab char"?: boolean;
|
||||
|
|
|
|||
|
|
@ -25,11 +25,7 @@ export class CopierMessages {
|
|||
o.e ?? 'unknown error'
|
||||
);
|
||||
|
||||
static INFO_CopyingProject = SevInfo | 0x0002;
|
||||
static Info_CopyingProject = (o:{type: string, id: string}) => m(
|
||||
this.INFO_CopyingProject,
|
||||
`Copying project of type ${def(o.type)} with id ${def(o.id)}`
|
||||
);
|
||||
// unused 0x0002 (was INFO_CopyingProject, now in InfrastructureMessages)
|
||||
|
||||
static ERROR_CannotCreateFolder = SevError | 0x0003;
|
||||
static Error_CannotCreateFolder = (o:{folderName:string, e: any}) => CompilerMessageSpecWithException(
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageSpec as m, CompilerMessageDef as def, CompilerMessageSpecWithException } from "@keymanapp/developer-utils";
|
||||
|
||||
const Namespace = CompilerErrorNamespace.Generator;
|
||||
const SevInfo = CompilerErrorSeverity.Info | Namespace;
|
||||
// const SevInfo = CompilerErrorSeverity.Info | Namespace;
|
||||
// const SevHint = CompilerErrorSeverity.Hint | Namespace;
|
||||
const SevWarn = CompilerErrorSeverity.Warn | Namespace;
|
||||
const SevError = CompilerErrorSeverity.Error | Namespace;
|
||||
|
|
@ -23,11 +23,7 @@ export class GeneratorMessages {
|
|||
o.e ?? 'unknown error',
|
||||
);
|
||||
|
||||
static INFO_GeneratingProject = SevInfo | 0x0002;
|
||||
static Info_GeneratingProject = (o:{type: string, id: string}) => m(
|
||||
this.INFO_GeneratingProject,
|
||||
`Generating project of type ${def(o.type)} with id ${def(o.id)}`
|
||||
);
|
||||
// unused 0x0002 (was INFO_GeneratingProject, now in InfrastructureMessages)
|
||||
|
||||
static ERROR_CannotCreateFolder = SevError | 0x0003;
|
||||
static Error_CannotCreateFolder = (o:{folderName:string, e: any}) => CompilerMessageSpecWithException(
|
||||
|
|
|
|||
|
|
@ -737,7 +737,19 @@ export class KmnCompilerMessages {
|
|||
static HINT_IndexStoreLong = SevHint | 0x0B0;
|
||||
static Hint_IndexStoreLong = () => m(
|
||||
this.HINT_IndexStoreLong,
|
||||
`The store referenced in index() is longer than the store referenced in any()`
|
||||
`The store referenced in index() is longer than the store referenced in any()`,
|
||||
);
|
||||
|
||||
static ERROR_ContextExCannotReferenceIf = SevError | 0x0B1;
|
||||
static Error_ContextExCannotReferenceIf = () => m(
|
||||
this.ERROR_ContextExCannotReferenceIf,
|
||||
`The offset in context() points to a non-character if() statement in the context`,
|
||||
);
|
||||
|
||||
static ERROR_ContextExCannotReferenceNul = SevError | 0x0B2;
|
||||
static Error_ContextExCannotReferenceNul = () => m(
|
||||
this.ERROR_ContextExCannotReferenceNul,
|
||||
`The offset in context() points to a non-character nul statement in the context`,
|
||||
);
|
||||
|
||||
static FATAL_BufferOverflow = SevFatal | 0x0C0;
|
||||
|
|
|
|||
|
|
@ -643,7 +643,7 @@ export function JavaScript_OutputString(fk: KMX.KEYBOARD, FTabStops: string, fkp
|
|||
let recContext = ExpandSentinel(fk, pwszContext, x);
|
||||
|
||||
if(isKeyboardVersion10OrLater()) {
|
||||
if(recContext.IsSentinel && [KMX.KMXFile.CODE_NUL, KMX.KMXFile.CODE_IFOPT, KMX.KMXFile.CODE_IFSYSTEMSTORE].includes(recContext.Code)) {
|
||||
if(recContext.IsSentinel && [KMX.KMXFile.CODE_IFOPT, KMX.KMXFile.CODE_IFSYSTEMSTORE].includes(recContext.Code)) {
|
||||
Result--;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
11
developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_contextex_cannot_reference_if.kmn
vendored
Normal file
11
developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_contextex_cannot_reference_if.kmn
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
store(&TARGETS) 'any'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
store(cons) 'bcd'
|
||||
store(ifx) '1'
|
||||
|
||||
group(main) using keys
|
||||
|
||||
c ERROR_ContextExCannotReferenceIf:
|
||||
if(ifx = '1') any(cons) + 'd' > context(1)
|
||||
10
developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_contextex_cannot_reference_nul.kmn
vendored
Normal file
10
developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_contextex_cannot_reference_nul.kmn
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
store(&TARGETS) 'any'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
store(cons) 'bcd'
|
||||
|
||||
group(main) using keys
|
||||
|
||||
c ERROR_ContextExCannotReferenceNul:
|
||||
nul any(cons) + 'a' > 'x' context(1)
|
||||
12
developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_contextex_has_invalid_offset_if.kmn
vendored
Normal file
12
developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_contextex_has_invalid_offset_if.kmn
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
store(&TARGETS) 'any'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
store(cons) 'bcd'
|
||||
store(ifx) '1'
|
||||
|
||||
group(main) using keys
|
||||
|
||||
c ERROR_ContextExHasInvalidOffset: `context(3)` should be `context(2)`
|
||||
if(ifx='1') any(cons) + 'a' > 'x' context(3) 'z'
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
store(&TARGETS) 'any'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
store(cons) 'bcd'
|
||||
|
||||
group(main) using keys
|
||||
|
||||
c ERROR_ContextExHasInvalidOffset: `context(2)` is longer than range
|
||||
any(cons) + 'a' > 'x' context(2) 'z'
|
||||
|
||||
12
developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_contextex_has_invalid_offset_nul.kmn
vendored
Normal file
12
developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_contextex_has_invalid_offset_nul.kmn
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
store(&TARGETS) 'any'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
store(cons) 'bcd'
|
||||
store(ifx) '1'
|
||||
|
||||
group(main) using keys
|
||||
|
||||
c ERROR_ContextExHasInvalidOffset: `context(3)` should be `context(2)`
|
||||
nul any(cons) + 'a' > 'x' context(3) 'z'
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
store(&TARGETS) 'any'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
store(cons) 'bcd'
|
||||
|
||||
group(main) using keys
|
||||
|
||||
c ERROR_ContextExHasInvalidOffset: `context(0)` is lower than 1
|
||||
any(cons) + 'a' > 'x' context(0) 'z'
|
||||
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
c test_index_12980 generated from template at 2025-01-22 13:25:35
|
||||
c Validates index() offsets when used with if() - see #12980
|
||||
c Test with v10+ kmw compiler
|
||||
|
||||
store(&VERSION) '14.0'
|
||||
store(&NAME) 'test_index_12980'
|
||||
store(&NAME) 'test_index_12980_v10'
|
||||
store(&TARGETS) 'web'
|
||||
|
||||
|
||||
22
developer/src/kmc-kmn/test/fixtures/kmw/test_index_12980_v9.kmn
vendored
Normal file
22
developer/src/kmc-kmn/test/fixtures/kmw/test_index_12980_v9.kmn
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
c test_index_12980 generated from template at 2025-01-22 13:25:35
|
||||
c Validates index() offsets when used with if() - see #12980
|
||||
c Test with v9 KMW compiler (does not support notany)
|
||||
|
||||
store(&VERSION) '9.0'
|
||||
store(&NAME) 'test_index_12980_v9'
|
||||
store(&TARGETS) 'web'
|
||||
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
||||
store(a) '1'
|
||||
store(b) '2'
|
||||
store(e) '5'
|
||||
store(f) '6'
|
||||
|
||||
if(&platform = 'web') any(a) + 'a' > context 'A'
|
||||
if(&platform = 'web') any(b) + 'b' > context(2) 'B'
|
||||
nul any(e) + 'e' > context 'E'
|
||||
nul any(f) + 'f' > context(2) 'F'
|
||||
|
|
@ -218,29 +218,56 @@ describe('KeymanWeb Compiler', function() {
|
|||
assert.lengthOf(callbacks.messages, 2);
|
||||
});
|
||||
|
||||
it('should generate correct index offsets (#12980) for context and context(n) when building a KMW keyboard', async function() {
|
||||
const filenames = generateTestFilenames('test_index_12980');
|
||||
|
||||
function doMatchKIO(data: string, s: string, offset: number) {
|
||||
const regex = new RegExp('k\\.KIO\\(-1,this\\.'+s+',(.+?),t\\);');
|
||||
const m = regex.exec(data);
|
||||
assert.isNotNull(m, `Could not find match for ${s}`);
|
||||
assert.equal(m[1], offset.toString(), `Expected '${s}' usage to have an index parameter == 1`);
|
||||
}
|
||||
|
||||
it('should generate correct index offsets (#12980) for context and context(n) when building a KMW keyboard with v9 target', async function() {
|
||||
const filenames = generateTestFilenames('test_index_12980_v9');
|
||||
|
||||
let result = await kmnCompiler.run(filenames.source, null);
|
||||
assert.isNotNull(result);
|
||||
const data = new TextDecoder('utf-8').decode(result.artifacts.js.data);
|
||||
|
||||
function doMatchKIO(s: string) {
|
||||
const regex = new RegExp('k\\.KIO\\(-1,this\\.'+s+',(.+?),t\\);');
|
||||
const m = regex.exec(data);
|
||||
assert.isNotNull(m, `Could not find match for ${s}`);
|
||||
assert.equal(m[1], '1', `Expected '${s}' usage to have an index parameter == 1`);
|
||||
if(debug) {
|
||||
console.log(result.artifacts.js.filename);
|
||||
await kmnCompiler.write(result.artifacts);
|
||||
}
|
||||
|
||||
doMatchKIO('s_a_6');
|
||||
doMatchKIO('s_b_7');
|
||||
// v9 targets should match
|
||||
assert.match(data, /k\.KO\(-1,t,"A"\);/);
|
||||
assert.match(data, /k\.KIO\(1,this.s_b_7,1,t\);/);
|
||||
assert.match(data, /k\.KO\(-1,t,"E"\);/);
|
||||
assert.match(data, /k\.KIO\(1,this.s_f_9,1,t\);/);
|
||||
});
|
||||
|
||||
// matches the 4 notany() rules
|
||||
const matches = [...data.matchAll(/k\.KCXO\(-1,t,1,1\);/g)];
|
||||
assert.lengthOf(matches, 4);
|
||||
it('should generate correct index offsets (#12980) for context and context(n) when building a KMW keyboard with v10+ target', async function() {
|
||||
const filenames = generateTestFilenames('test_index_12980_v10');
|
||||
|
||||
doMatchKIO('s_e_10');
|
||||
doMatchKIO('s_f_11');
|
||||
let result = await kmnCompiler.run(filenames.source, null);
|
||||
assert.isNotNull(result);
|
||||
const data = new TextDecoder('utf-8').decode(result.artifacts.js.data);
|
||||
|
||||
if(debug) {
|
||||
console.log(result.artifacts.js.filename);
|
||||
await kmnCompiler.write(result.artifacts);
|
||||
}
|
||||
|
||||
doMatchKIO(data, 's_a_6', 1); //if() has index 1
|
||||
doMatchKIO(data, 's_b_7', 1); //if() has index 1
|
||||
|
||||
// matches the 2 notany() rules for if() and 2 for nul
|
||||
const matches_if = [...data.matchAll(/k\.KCXO\(-1,t,1,1\);/g)];
|
||||
assert.lengthOf(matches_if, 2);
|
||||
const matches_nul = [...data.matchAll(/k\.KCXO\(-1,t,2,2\);/g)];
|
||||
assert.lengthOf(matches_nul, 2);
|
||||
|
||||
doMatchKIO(data, 's_e_10', 2);
|
||||
doMatchKIO(data, 's_f_11', 2);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -185,7 +185,6 @@ describe('KmnCompilerMessages', function () {
|
|||
|
||||
it('should generate WARN_IndexStoreShort if a store referenced in index() is shorter than the corresponding any() store', async function() {
|
||||
await testForMessage(this, ['keyboards', 'warn_index_store_short.kmn'], KmnCompilerMessages.WARN_IndexStoreShort);
|
||||
callbacks.clear();
|
||||
await testForMessage(this, ['keyboards', 'warn_index_store_short_key.kmn'], KmnCompilerMessages.WARN_IndexStoreShort);
|
||||
});
|
||||
|
||||
|
|
@ -193,8 +192,28 @@ describe('KmnCompilerMessages', function () {
|
|||
|
||||
it('should generate HINT_IndexStoreLong if a store referenced in index() is longer than the corresponding any() store', async function() {
|
||||
await testForMessage(this, ['keyboards', 'hint_index_store_long.kmn'], KmnCompilerMessages.HINT_IndexStoreLong);
|
||||
callbacks.clear();
|
||||
await testForMessage(this, ['keyboards', 'hint_index_store_long_key.kmn'], KmnCompilerMessages.HINT_IndexStoreLong);
|
||||
});
|
||||
|
||||
// ERROR_ContextExHasInvalidOffset
|
||||
|
||||
it('should generate ERROR_ContextExHasInvalidOffset if the offset is lower than 1 or greater than the context length', async function() {
|
||||
await testForMessage(this, ['invalid-keyboards', 'error_contextex_has_invalid_offset_if.kmn'], KmnCompilerMessages.ERROR_ContextExHasInvalidOffset);
|
||||
await testForMessage(this, ['invalid-keyboards', 'error_contextex_has_invalid_offset_long.kmn'], KmnCompilerMessages.ERROR_ContextExHasInvalidOffset);
|
||||
await testForMessage(this, ['invalid-keyboards', 'error_contextex_has_invalid_offset_nul.kmn'], KmnCompilerMessages.ERROR_ContextExHasInvalidOffset);
|
||||
await testForMessage(this, ['invalid-keyboards', 'error_contextex_has_invalid_offset_zero.kmn'], KmnCompilerMessages.ERROR_ContextExHasInvalidOffset);
|
||||
});
|
||||
|
||||
// ERROR_ContextExCannotReferenceIf
|
||||
|
||||
it('should generate ERROR_ContextExCannotReferenceIf if the offset points to an if() statement', async function() {
|
||||
await testForMessage(this, ['invalid-keyboards', 'error_contextex_cannot_reference_if.kmn'], KmnCompilerMessages.ERROR_ContextExCannotReferenceIf);
|
||||
});
|
||||
|
||||
// ERROR_ContextExCannotReferenceNul
|
||||
|
||||
it('should generate ERROR_ContextExCannotReferenceNul if the offset points to a nul statement', async function() {
|
||||
await testForMessage(this, ['invalid-keyboards', 'error_contextex_cannot_reference_nul.kmn'], KmnCompilerMessages.ERROR_ContextExCannotReferenceNul);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ function do_build() {
|
|||
}
|
||||
|
||||
function do_build_abnf() {
|
||||
# we convert over all abnf files found.
|
||||
# we convert over all abnf files found.
|
||||
local file
|
||||
for file in "$KEYMAN_ROOT/resources/standards-data/ldml-keyboards"/*/abnf/*.abnf; do
|
||||
local cldrver="$(basename $(dirname $(dirname "$file")))"
|
||||
|
|
@ -82,7 +82,7 @@ builder_run_action configure do_configure
|
|||
builder_run_action build do_build
|
||||
builder_run_action build-fixtures do_build_fixtures
|
||||
builder_run_action api api-extractor run --local --verbose
|
||||
builder_run_action test builder_do_typescript_tests
|
||||
builder_run_action test builder_do_typescript_tests 75
|
||||
|
||||
#-------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -135,7 +135,9 @@ export class LdmlKeyboardVisualKeyboardCompiler {
|
|||
|
||||
private getDisplayFromKey(keydef: KMXPlus.KeysKeys, source: KMXPlus.KMXPlusData) {
|
||||
const display = source.disp?.disps?.find(d => d.id.value == keydef.id.value || d.to.value == keydef.to.value);
|
||||
return display?.display.value ?? keydef.to.value;
|
||||
const value = display?.display.value ?? keydef.to.value;
|
||||
// strip markers from the output (these are valid in keydef.to, but not in display.display, nor in kvk)
|
||||
return value.replaceAll(/\uffff\u0008./g, '');
|
||||
}
|
||||
|
||||
private translateLayerModifiersToVisualKeyboardShift(modifiers: number): VisualKeyboard.VisualKeyboardShiftState {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue