chore(web): Merge branch 'beta' into fix/web/4026-rescale-tall-keys

This commit is contained in:
jahorton 2021-01-21 10:32:36 +07:00
commit 693018a2e2
124 changed files with 4532 additions and 566 deletions

View file

@ -1,5 +1,41 @@
# Keyman Version History
## 14.0.226 beta 2021-01-20
* fix(windows, common/core/desktop): context mismatch with if and dk (#4276)
* chore(common): Check in crowdin files for de (German) (#4295)
* feat(android/app): Add option to change display language (#4261)
* fix(developer/compilers): fixes developer build breakage from #4291 (#4299)
* fix(android/app): Handle keyman protocol from external browser (#4292)
## 14.0.225 beta 2021-01-19
* fix(android/samples): Fix Sentry dependencies (#4267)
* fix(web): better font-wait null guards (#4286)
* fix(web): uses CSS line-height to vertically center oversized OSK keycaps (#4255)
* fix(developer): disable TSentryClient on WINE (#4274)
* fix(web): no key previews for special keys reliant on keyboard-specific OSK font (#4282)
* fix(web): default kbd ui name is now generic (#4293)
* fix(developer/compilers): fixes dependency versioning on alpha, beta tiers (#4291)
## 14.0.224 beta 2021-01-18
* fix(android): Popup misalignments and compatibility with WeChat, Telegram (#4254)
* chore(web): sample pages now wait on init's promise for keyboard loading (#4253)
## 14.0.223 beta 2021-01-15
* fix(windows): typo in font helper string (#4251)
* chore(android/app): Add Obolo language from crowdin (#4256)
* fix(web): unexpected errors in OSK / banner position calculations (#4238)
* chore(linux): Improve version number for debian package (#4258)
## 14.0.222 beta 2021-01-14
* chore(common): Check in crowdin files for km (Khmer) (#4228)
* fix(web): OSK key preview positioning (#4241)
* fix(web): disables predictive text on Opera mini (#4243)
## 14.0.221 beta 2021-01-13
* fix(windows): fix menu popup position (#4175)

View file

@ -1 +1 @@
14.0.222
14.0.227

View file

@ -261,6 +261,12 @@
android:parentActivityName=".KeymanSettingsActivity"
android:launchMode="singleTask"
android:theme="@style/AppTheme.Base" />
<activity
android:name=".KeymanSettingsLocalizeActivity"
android:label="@string/app_name"
android:parentActivityName=".KeymanSettingsActivity"
android:launchMode="singleTask"
android:theme="@style/AppTheme.Base" />
<activity
android:name=".LanguagesSettingsActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize"

View file

@ -22,11 +22,13 @@ import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.widget.FrameLayout;
import io.sentry.android.core.SentryAndroid;
import io.sentry.Sentry;
@ -176,6 +178,16 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
KMManager.onConfigurationChanged(newConfig);
}
@Override
public void onConfigureWindow(Window win, boolean isFullscreen, boolean isCandidatesOnly) {
super.onConfigureWindow(win, isFullscreen, isCandidatesOnly);
// We don't currently use isFullscreen or isCandidatesOnly; we always want to MATCH_PARENT,
// unlike the default for height which is WRAP_CONTENT. We then adjust the touchable area
// in `onCalculateInsets`
win.setLayout(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
}
@Override
public void onComputeInsets(InputMethodService.Insets outInsets) {
super.onComputeInsets(outInsets);
@ -192,6 +204,7 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
int bannerHeight = KMManager.getBannerHeight(this);
int kbHeight = KMManager.getKeyboardHeight(this);
outInsets.contentTopInsets = inputViewHeight - bannerHeight - kbHeight;
outInsets.visibleTopInsets = outInsets.contentTopInsets;
outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
outInsets.touchableRegion.set(0, outInsets.contentTopInsets, size.x, size.y);
}

View file

@ -37,9 +37,10 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import com.tavultesoft.kmea.BaseActivity;
import com.tavultesoft.kmea.util.KMLog;
public class BookmarksActivity extends AppCompatActivity {
public class BookmarksActivity extends BaseActivity {
private static final String TAG = "BookmarksActivity";
private static ListView listView;
private static ArrayList<HashMap<String, String>> list = null;

View file

@ -8,6 +8,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import com.tavultesoft.kmea.BaseActivity;
import com.tavultesoft.kmea.KMManager;
import com.tavultesoft.kmea.data.Keyboard;
@ -29,7 +30,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.widget.TextView;
public class GetStartedActivity extends AppCompatActivity {
public class GetStartedActivity extends BaseActivity {
private static ListView listView = null;
private static ArrayList<HashMap<String, String>> list = null;

View file

@ -4,6 +4,7 @@
package com.tavultesoft.kmapro;
import com.tavultesoft.kmea.BaseActivity;
import com.tavultesoft.kmea.BuildConfig;
import com.tavultesoft.kmea.KMManager;
import com.tavultesoft.kmea.KMManager.FormFactor;
@ -20,7 +21,7 @@ import android.widget.TextView;
import android.annotation.SuppressLint;
import androidx.appcompat.app.AppCompatActivity;
public class InfoActivity extends AppCompatActivity {
public class InfoActivity extends BaseActivity {
private final static String TAG = "InfoActivity";
private WebView webView;
private final String HELP_PRODUCTION_HOST = "help.keyman.com";

View file

@ -19,6 +19,7 @@ import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.appcompat.app.AppCompatActivity;
import com.tavultesoft.kmea.BaseActivity;
import com.tavultesoft.kmea.KMManager;
import com.tavultesoft.kmea.util.KMPLink;
@ -27,7 +28,7 @@ import java.util.regex.Pattern;
import static android.app.Application.getProcessName;
public class KMPBrowserActivity extends AppCompatActivity {
public class KMPBrowserActivity extends BaseActivity {
private static final String TAG = "KMPBrowserActivity";
// URL for keyboard search web page presented to user when they add a keyboard in the app.

View file

@ -2,10 +2,12 @@ package com.tavultesoft.kmapro;
import android.os.Bundle;
import com.tavultesoft.kmea.BaseActivity;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
public class KeymanSettingsActivity extends AppCompatActivity {
public class KeymanSettingsActivity extends BaseActivity {
protected static final String installedLanguagesKey = "InstalledLanguages";
protected static final String installKeyboardOrDictionaryKey = "InstallKeyboardOrDictionary";
protected static final String showBannerKey = "ShowBanner";
@ -20,6 +22,7 @@ public class KeymanSettingsActivity extends AppCompatActivity {
setContentView(R.layout.keyman_settings_layout);
Toolbar toolbar = (Toolbar) findViewById(R.id.keyman_settings_toolbar);
toolbar.setTitleTextColor(getResources().getColor(android.R.color.white));
toolbar.setTitle(R.string.keyman_settings);
setSupportActionBar(toolbar);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);

View file

@ -1,3 +1,7 @@
/**
* Copyright (C) 2020-2021 SIL International. All rights reserved.
*/
package com.tavultesoft.kmapro;
import android.content.Context;
@ -12,6 +16,7 @@ import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import com.tavultesoft.kmea.DisplayLanguages;
import com.tavultesoft.kmea.KMManager;
import com.tavultesoft.kmea.data.Dataset;
@ -19,7 +24,7 @@ public class KeymanSettingsFragment extends PreferenceFragmentCompat {
private static final String TAG = "SettingsFragment";
private static Context context;
private Preference languagesPreference, installKeyboardOrDictionary;
private Preference languagesPreference, installKeyboardOrDictionary, displayLanguagePreference;
private CheckBoxPreference setSystemKeyboardPreference;
private CheckBoxPreference setDefaultKeyboardPreference;
@ -46,6 +51,13 @@ public class KeymanSettingsFragment extends PreferenceFragmentCompat {
Intent installIntent = new Intent(context, KeymanSettingsInstallActivity.class);
installKeyboardOrDictionary.setIntent(installIntent);
displayLanguagePreference = new Preference(context);
displayLanguagePreference.setKey(DisplayLanguages.displayLanguageKey);
displayLanguagePreference.setTitle(getString(R.string.change_display_language));
displayLanguagePreference.setWidgetLayoutResource(R.layout.preference_translate_icon_layout);
Intent displayLanguageIntent = new Intent(context, KeymanSettingsLocalizeActivity.class);
displayLanguagePreference.setIntent(displayLanguageIntent);
/*
Automatically does the following:
SharedPreferences.Editor editor = prefs.edit();
@ -106,6 +118,7 @@ public class KeymanSettingsFragment extends PreferenceFragmentCompat {
screen.addPreference(languagesPreference);
screen.addPreference(installKeyboardOrDictionary);
screen.addPreference(displayLanguagePreference);
screen.addPreference(setSystemKeyboardPreference);
screen.addPreference(setDefaultKeyboardPreference);

View file

@ -0,0 +1,106 @@
/**
* Copyright (C) 2021 SIL International. All rights reserved.
*/
package com.tavultesoft.kmapro;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.preference.PreferenceManager;
import com.tavultesoft.kmea.DisplayLanguages;
import java.util.ArrayList;
import java.util.HashMap;
public class KeymanSettingsLocalizeActivity extends AppCompatActivity {
private static final String TAG = "SettingsLocalizeActivity";
private static ArrayList<HashMap<String, String>> localizeOptionList = null;
private static Typeface titleFont = null;
private static final String titleKey = "title";
private static final String subtitleKey = "subtitle";
private static final String iconKey = "icon";
private static Context context;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
context = this;
setContentView(R.layout.activity_list_layout);
final Toolbar toolbar = findViewById(R.id.list_toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setDisplayShowTitleEnabled(false);
final ListView listView = findViewById(com.tavultesoft.kmea.R.id.listView);
Bundle bundle = getIntent().getExtras();
final TextView textView = findViewById(R.id.bar_title);
textView.setText(getString(R.string.change_display_language));
if (titleFont != null) {
textView.setTypeface(titleFont, Typeface.BOLD);
}
localizeOptionList = new ArrayList<>();
final String noIcon = "0";
for(DisplayLanguages.DisplayLanguageType l: DisplayLanguages.DisplayLanguages) {
HashMap<String, String> hashMap = new HashMap<>();
hashMap.put(titleKey, l.getLanguageName());
hashMap.put(iconKey, noIcon);
localizeOptionList.add(hashMap);
}
String[] from = new String[]{titleKey, subtitleKey, iconKey};
int[] to = new int[]{com.tavultesoft.kmea.R.id.text1, com.tavultesoft.kmea.R.id.image1};
ListAdapter adapter = new SimpleAdapter(context, localizeOptionList, com.tavultesoft.kmea.R.layout.list_row_layout1, from, to) {
};
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// Store the BCP-47 language tag in shared preference
if (position >= 0 && position < DisplayLanguages.DisplayLanguages.length) {
DisplayLanguages.DisplayLanguageType l = DisplayLanguages.DisplayLanguages[position];
String languageTag = l.getLanguageTag();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = prefs.edit();
editor.putString(DisplayLanguages.displayLanguageKey, languageTag);
editor.commit();
// Restart the app to use selected locale
Intent intent = new Intent(context, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
});
}
@Override
public boolean onSupportNavigateUp() {
super.onBackPressed();
return true;
}
}

View file

@ -1,5 +1,5 @@
/**
* Copyright (C) 2018-2020 SIL International. All rights reserved.
* Copyright (C) 2018-2021 SIL International. All rights reserved.
*/
package com.tavultesoft.kmapro;
@ -17,6 +17,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.tavultesoft.kmea.BaseActivity;
import com.tavultesoft.kmea.KMKeyboardDownloaderActivity;
import com.tavultesoft.kmea.KMManager;
import com.tavultesoft.kmea.KMManager.KeyboardType;
@ -38,7 +39,6 @@ import com.tavultesoft.kmea.util.KMPLink;
import android.Manifest;
import android.app.ProgressDialog;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
@ -92,7 +92,7 @@ import android.widget.Toast;
import io.sentry.android.core.SentryAndroid;
public class MainActivity extends AppCompatActivity implements OnKeyboardEventListener, OnKeyboardDownloadEventListener,
public class MainActivity extends BaseActivity implements OnKeyboardEventListener, OnKeyboardDownloadEventListener,
ActivityCompat.OnRequestPermissionsResultCallback {
public static Context context;
@ -256,13 +256,6 @@ public class MainActivity extends AppCompatActivity implements OnKeyboardEventLi
KMKeyboardDownloaderActivity.addKeyboardDownloadEventListener(this);
PackageActivity.addKeyboardDownloadEventListener(this);
// Get calling activity
ComponentName component = this.getCallingActivity();
String caller = null;
if (component != null) {
caller = component.getClassName();
}
Intent intent = getIntent();
data = intent.getData();
@ -285,10 +278,8 @@ public class MainActivity extends AppCompatActivity implements OnKeyboardEventLi
downloadKMP(scheme);
break;
case "keyman" :
// Only accept download links from Keyman browser activities
if (KMPLink.isKeymanDownloadLink(data.toString()) && caller != null &&
(caller.equalsIgnoreCase("com.tavultesoft.kmea.KMPBrowserActivity") ||
caller.equalsIgnoreCase("com.tavultesoft.kmapro.WebBrowserActivity"))) {
// TODO: Only accept download links from Keyman browser activities when universal links work
if (KMPLink.isKeymanDownloadLink(data.toString())) {
// Convert opaque URI to hierarchical URI so the query parameters can be parsed
Builder builder = new Uri.Builder();
@ -298,6 +289,10 @@ public class MainActivity extends AppCompatActivity implements OnKeyboardEventLi
.encodedQuery(data.getEncodedQuery());
data = Uri.parse(builder.build().toString());
downloadKMP(scheme);
} else if (KMPLink.isLegacyKeymanDownloadLink(data.toString())) {
link = data.toString();
data = KMPLink.getLegacyKeyboardDownloadLink(link);
downloadKMP(scheme);
} else {
String msg = "Unrecognized scheme: " + scheme;
KMLog.LogError(TAG, msg);

View file

@ -10,6 +10,7 @@ import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import com.tavultesoft.kmea.BaseActivity;
import com.tavultesoft.kmea.KMManager;
import com.tavultesoft.kmea.util.KMPLink;
@ -48,7 +49,7 @@ import androidx.appcompat.app.AppCompatActivity;
import static android.app.Application.getProcessName;
public class WebBrowserActivity extends AppCompatActivity {
public class WebBrowserActivity extends BaseActivity {
private static final String TAG = "WebBrowserActivity";
private WebView webView;
private EditText addressField;

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

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

View file

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Context: Menu Action -->
<string name="action_share" comment="Menu action to send text content to another app">Che</string>
<!-- Context: Menu Action -->
<string name="action_web" comment="Menu action to open Keyman browser">Òwọlọ olik-etip</string>
<!-- Context: Menu Action -->
<string name="action_text_size" comment="Menu action to adjust text size">Okike inu-nge</string>
<!-- Context: Menu Action -->
<string name="action_overflow" comment="Show additional menu items">Ofifi</string>
<!-- Context: Menu Action -->
<string name="action_clear_text" comment="Menu action to erase text">Chọk inu-nge</string>
<!-- Context: Menu Action -->
<string name="action_info" comment="Menu action to display Keyman help page">Etip</string>
<!-- Context: Menu Action -->
<string name="action_settings" comment="Menu action to open Settings menu">Onineen̄</string>
<!-- Context: Menu Action -->
<string name="action_install_updates" comment="Menu notification that keyboard or dictionary updates available">Wop aya inu</string>
<!-- Context: Title -->
<string name="title_version" comment="Title of Keyman for Android version">Òsolek: %1$s</string>
<!-- Context: Textfield prompt (&#8230; is the ellipsis character "...") -->
<string name="textview_hint" comment="Prompt to start typing">Bene ge me ere keyi&#8230;</string>
<!-- Context: Splash screen (version/copyright info currently disabled -->
<!-- Context: Splash screen (version/copyright info currently disabled -->
<!-- Context: Text Size dialog -->
<string name="text_size" comment="Current text size (number)">Okike inu-nge: %1$d</string>
<!-- Context: Text Size dialog -->
<string name="ic_text_size_up" comment="Make text bigger">Rọ inu-nge imin</string>
<!-- Context: Text Size dialog -->
<string name="ic_text_size_down" comment="Make text smaller">Rọ inu-nge isip</string>
<!-- Context: Clear Text dialog -->
<string name="all_text_will_be_cleared" comment="Erase all the text">\nMêchọk otutuuk inu-nge\n</string>
<!-- Context: Get Started menu -->
<string name="get_started" comment="Menu for getting started">Bene si ikwaan̄</string>
<!-- Context: Get Started menu -->
<string name="add_a_keyboard" comment="Menu item to add a keyboard">Tap uwot-nge eyi usem kwun̄</string>
<!-- Context: Get Started menu -->
<string name="enable_system_keyboard" comment="Menu item to enable Keyman as system keyboard">Rọ mè Keyman ibene ikisi ikwaan̄ me otutuuk emen okwukwut</string>
<!-- Context: Get Started menu -->
<string name="set_keyman_as_default" comment="Menu item to set Keyman as default system keyboard">Rọ Keyman ire ọ̀kpọọn̄ uwot-nge</string>
<!-- Context: Get Started menu -->
<string name="more_info" comment="Open the Keyman for Android help page">Ofifi etip</string>
<!-- Context: Get Started menu -->
<string name="show_get_started" comment="Show the &quot;Get Started&quot; menu on startup">Jeen̄ \"%1$s\" me ibebene</string>
<!-- Context: Android Storage Permission -->
<string name="request_storage_permission" comment="Request storage permission to access keyboard packages">Ufuna òkọt òwop ekwu, nyi Keyman unye mè ifuk/ige inu me okwukwut.</string>
<!-- Context: Android Storage Permission -->
<string name="storage_permission_denied" comment="Keyboard package installation may fail since Android storage permission not granted">Kpenyi unye ibe itap inu me emen ekwu. Ìkpokọt iwop ekwu uwot-nge</string>
<!-- Context: Keyman Settings menu -->
<string name="keyman_settings" comment="Settings menu title">Onineen̄</string>
<!-- Context: Keyman Settings menu -->
<plurals name="installed_languages">
<item quantity="one">Usem ewopbe (%1$d)</item>
<item quantity="other">Ebi kè usem ewopbe (%1$d)</item>
</plurals>
<!-- Context: Keyman Settings menu -->
<string name="install_keyboard_or_dictionary" comment="Menu item to install keyboard or dictionary">Wop Uwot-nge mè ìre Ikpa-usem</string>
<!-- Context: Keyman Settings menu -->
<string name="show_banner" comment="text suggestions banner">Kijeen̄ egop mgbọ geelek</string>
<!-- Context: Keyman Settings menu -->
<string name="show_banner_on" comment="Description when toggle is on">Ọmọ me otitọt</string>
<!-- Context: Keyman Settings menu -->
<string name="show_banner_off" comment="Description when toggle is off">Ire iniin̄, jeen̄ mgbọ echieekbe ibe òjeen̄ ikọ òkinunu</string>
<!-- Context: Keyman Settings menu -->
<string name="show_send_crash_report" comment="permission for sending crash reports">Tele ibe isa olik-etip iria etip òfolek ufialek ekwu</string>
<!-- Context: Keyman Settings menu -->
<string name="show_send_crash_report_on" comment="Description when toggle is on">Ire ichili ito, môria erip òfolek ufialek ekwu</string>
<!-- Context: Keyman Settings menu -->
<string name="show_send_crash_report_off" comment="Description when toggle is off">Ire ichit, ìkporia erip òfolek ufialek ekwu</string>
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
<string name="install_from_keyman_dot_com" comment="Install package from Keyman server">Bọkọ me keyman.com wop</string>
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
<string name="install_from_local_file" comment="Install package file from local device">San̄a me emen okwukwut wop</string>
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
<string name="install_from_other_device" comment="Scan QR Code">Bọkọ me ofifi okwukwut wop</string>
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
<string name="add_languages_to_installed_keyboard" comment="Install additional languages from a keyboard package">Tap usem sọkọ me lek uwot-nge ewopbe</string>
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
<string name="add_language_subtext" comment="Additional information for adding another language">(inan̄a me ekwu eyi uwot-nge)</string>
<!-- Context: Select Package and Select Languages menu -->
<string name="title_select_keyboard_package_list" comment="Select a keyboard package to add languages">Gobo Ekwu Uwot-nge</string>
<!-- Context: Select Package and Select Languages menu -->
<string name="title_select_languages_for_package" comment="Select language names from the keyboard package">Gobo usem nyi %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">Mîtap usem %1$s me lek %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">Mîwuuk lek itap otutuuk usem iso</string>
<!-- Context: Keyman Web Browser -->
<string name="hint_text" comment="Prompt to type in the browser search bar">Wèek sà ìre ge iman̄ akpatan̄</string>
<!-- Context: Keyman Web Browser -->
<string name="title_bookmarks" comment="Title for bookmarks">Iman̄-ikpa</string>
<!-- Context: Keyman Web Browser -->
<string name="no_bookmarks" comment="Text when bookmark list is empty">Kpunu iman̄-ikpa</string>
<!-- Context: Keyman Web Browser -->
<string name="add_bookmark" comment="Confirmation to add bookmark">Tap iman̄-ikpa</string>
<!-- Context: Keyman Web Browser -->
<string name="hint_title" comment="Page title for saved bookmark">Ibot</string>
<!-- Context: Keyman Web Browser -->
<string name="hint_url" comment="Page URL for saved bookmark">Iman̄ akpatan̄</string>
<!-- Context: KMP Package strings -->
<string name="title_package_failed_to_install" comment="Error dialog title when package failed to install">Kpekọt iwop ekwu %1$s</string>
<!-- Context: KMP Package strings -->
<string name="downloading_keyboard_package" comment="Confirmation to download keyboard package filename">Mêkibọkọ ekwu uwot-nge itap\n%1$s&#8230;</string>
<!-- Context: KMP Package strings -->
<string name="failed_to_extract" comment="Notification that keyboard package failed to unzip">Kpekọt isan̄a isibi</string>
<!-- Context: KMP Package strings -->
<string name="install_keyboard_package" comment="Title to install keyboard package">Wop Uwot-nge</string>
<!-- Context: KMP Package welcome.htm title -->
<string name="welcome_package" comment="Title to welcome.htm page (name and version)">Onu me %1$s</string>
<!-- Context: KMP Package strings -->
<string name="install_predictive_text_package" comment="Title to install dictionary package">Wop Ikpa-usem</string>
<!-- Context: KMP Package strings -->
<string name="no_new_touch_keyboards_to_install" comment="Notification when no touch-optimized keyboards can be installed">Ekwu uwot-nge ìkakaan̄ uwot-nge ekichabe ubọk eyi mêkọtbe iwop</string>
<!-- Context: KMP Package strings -->
<string name="no_new_predictive_text_to_install" comment="Notification when no dictionaries can be installed">Kpunu aya ikọ òkinunu eyi mêwopbe</string>
<!-- Context: KMP Package strings -->
<string name="no_targets_to_install" comment="Notification when a package doesn't contain keyboards or dictionaries">Kpunu uwot-nge sà ìre aya ikọ òkinunu eyi mêwopbe</string>
<!-- Context: KMP Package strings -->
<string name="no_associated_languages" comment="Notification when a keyboard package doesn't contain languages">Uwot-nge ìkakaan̄ usem òje me lek eyi mêkọtbe iwop</string>
<!-- Context: KMP Package strings -->
<string name="invalid_metadata" comment="kmp.json metadata file is invalid or missing in package">Metadata òkup me ekwu îlọ/îchep</string>
</resources>

View file

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Context: Menu Action -->
<string name="action_share" comment="Menu action to send text content to another app">Teilen</string>
<!-- Context: Menu Action -->
<string name="action_web" comment="Menu action to open Keyman browser">Web-Browser</string>
<!-- Context: Menu Action -->
<string name="action_text_size" comment="Menu action to adjust text size">Schriftgröße</string>
<!-- Context: Menu Action -->
<string name="action_overflow" comment="Show additional menu items">Mehr</string>
<!-- Context: Menu Action -->
<string name="action_clear_text" comment="Menu action to erase text">Text löschen</string>
<!-- Context: Menu Action -->
<string name="action_info" comment="Menu action to display Keyman help page">Info</string>
<!-- Context: Menu Action -->
<string name="action_settings" comment="Menu action to open Settings menu">Einstellungen</string>
<!-- Context: Menu Action -->
<string name="action_install_updates" comment="Menu notification that keyboard or dictionary updates available">Updates installieren</string>
<!-- Context: Title -->
<string name="title_version" comment="Title of Keyman for Android version">Version: %1$s</string>
<!-- Context: Textfield prompt (&#8230; is the ellipsis character "...") -->
<string name="textview_hint" comment="Prompt to start typing">Tippen Sie hier&#8230;</string>
<!-- Context: Splash screen (version/copyright info currently disabled -->
<!-- Context: Splash screen (version/copyright info currently disabled -->
<!-- Context: Text Size dialog -->
<string name="text_size" comment="Current text size (number)">Textgröße: %1$d</string>
<!-- Context: Text Size dialog -->
<string name="ic_text_size_up" comment="Make text bigger">Text größer</string>
<!-- Context: Text Size dialog -->
<string name="ic_text_size_down" comment="Make text smaller">Text kleiner</string>
<!-- Context: Clear Text dialog -->
<string name="all_text_will_be_cleared" comment="Erase all the text">\nDer gesamte Text wird gelöscht\n</string>
<!-- Context: Get Started menu -->
<string name="get_started" comment="Menu for getting started">Erste Schritte</string>
<!-- Context: Get Started menu -->
<string name="add_a_keyboard" comment="Menu item to add a keyboard">Tastatur für Ihre Sprache hinzufügen</string>
<!-- Context: Get Started menu -->
<string name="enable_system_keyboard" comment="Menu item to enable Keyman as system keyboard">Keyman als systemweite Tastatur aktivieren</string>
<!-- Context: Get Started menu -->
<string name="set_keyman_as_default" comment="Menu item to set Keyman as default system keyboard">Keyman als Standard-Tastatur festlegen</string>
<!-- Context: Get Started menu -->
<string name="more_info" comment="Open the Keyman for Android help page">Weitere Informationen</string>
<!-- Context: Get Started menu -->
<string name="show_get_started" comment="Show the &quot;Get Started&quot; menu on startup">Zeige \"%1$s\" beim Start</string>
<!-- Context: Android Storage Permission -->
<string name="request_storage_permission" comment="Request storage permission to access keyboard packages"> Um Tastaturpakete zu installieren, braucht Keyman die Berechtigung zum Lesen/Schreiben.</string>
<!-- Context: Android Storage Permission -->
<string name="storage_permission_denied" comment="Keyboard package installation may fail since Android storage permission not granted"> Zugriff auf den Speicher wurde abgelehnt. Möglicherweise funktioniert die Installation des Tastaturpakets nicht korrekt</string>
<!-- Context: Keyman Settings menu -->
<string name="keyman_settings" comment="Settings menu title">Einstellungen</string>
<!-- Context: Keyman Settings menu -->
<plurals name="installed_languages">
<item quantity="one">Installierte Sprachen (%1$d)</item>
<item quantity="other">Installierte Sprachen (%1$d)</item>
</plurals>
<!-- Context: Keyman Settings menu -->
<string name="install_keyboard_or_dictionary" comment="Menu item to install keyboard or dictionary">Tastatur oder Wörterbuch installieren</string>
<!-- Context: Keyman Settings menu -->
<string name="show_banner" comment="text suggestions banner">Banner immer anzeigen</string>
<!-- Context: Keyman Settings menu -->
<string name="show_banner_on" comment="Description when toggle is on">Nicht implementiert</string>
<!-- Context: Keyman Settings menu -->
<string name="show_banner_off" comment="Description when toggle is off">Wenn ausgeschaltet, wird nur angezeigt, wenn vorhersagender Text aktiviert ist</string>
<!-- Context: Keyman Settings menu -->
<string name="show_send_crash_report" comment="permission for sending crash reports">Erlaube das Senden von Fehlerberichten</string>
<!-- Context: Keyman Settings menu -->
<string name="show_send_crash_report_on" comment="Description when toggle is on">Wenn aktiviert werden Fehlerberichte gesendet</string>
<!-- Context: Keyman Settings menu -->
<string name="show_send_crash_report_off" comment="Description when toggle is off">Wenn deaktiviert werden Fehlerberichte nicht gesendet</string>
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
<string name="install_from_keyman_dot_com" comment="Install package from Keyman server">Von keyman.com installieren</string>
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
<string name="install_from_local_file" comment="Install package file from local device">Aus lokaler Datei installieren</string>
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
<string name="install_from_other_device" comment="Scan QR Code">Von einem anderen Gerät installieren</string>
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
<string name="add_languages_to_installed_keyboard" comment="Install additional languages from a keyboard package">Sprachen zur installierten Tastatur hinzufügen</string>
<!-- Context: Keyman Settings "Install Keyboard or Dictionary" menu -->
<string name="add_language_subtext" comment="Additional information for adding another language">(von Tastaturpaket)</string>
<!-- Context: Select Package and Select Languages menu -->
<string name="title_select_keyboard_package_list" comment="Select a keyboard package to add languages">Tastaturpaket auswählen</string>
<!-- Context: Select Package and Select Languages menu -->
<string name="title_select_languages_for_package" comment="Select language names from the keyboard package">Wählen Sie Sprachen für %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">Sprache %1$s zu %2$s hinzugefügt</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">Alle Sprachen bereits installiert</string>
<!-- Context: Keyman Web Browser -->
<string name="hint_text" comment="Prompt to type in the browser search bar">Suchen oder URL eingeben</string>
<!-- Context: Keyman Web Browser -->
<string name="title_bookmarks" comment="Title for bookmarks">Lesezeichen</string>
<!-- Context: Keyman Web Browser -->
<string name="no_bookmarks" comment="Text when bookmark list is empty">Keine Lesezeichen</string>
<!-- Context: Keyman Web Browser -->
<string name="add_bookmark" comment="Confirmation to add bookmark">Lesezeichen hinzufügen</string>
<!-- Context: Keyman Web Browser -->
<string name="hint_title" comment="Page title for saved bookmark">Titel</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">Paket %1$s konnte nicht installiert werden</string>
<!-- Context: KMP Package strings -->
<string name="downloading_keyboard_package" comment="Confirmation to download keyboard package filename">Tastaturpaket wird heruntergeladen\n%1$s&#8230;</string>
<!-- Context: KMP Package strings -->
<string name="failed_to_extract" comment="Notification that keyboard package failed to unzip">Entpacken fehlgeschlagen</string>
<!-- Context: KMP Package strings -->
<string name="install_keyboard_package" comment="Title to install keyboard package">Tastatur installieren</string>
<!-- Context: KMP Package welcome.htm title -->
<string name="welcome_package" comment="Title to welcome.htm page (name and version)">Willkommen bei %1$s</string>
<!-- Context: KMP Package strings -->
<string name="install_predictive_text_package" comment="Title to install dictionary package">Wörterbuch installieren</string>
<!-- Context: KMP Package strings -->
<string name="no_new_touch_keyboards_to_install" comment="Notification when no touch-optimized keyboards can be installed">Tastaturpaket enthält keine berührungsoptimierte Tastatur, die installiert werden könnte</string>
<!-- Context: KMP Package strings -->
<string name="no_new_predictive_text_to_install" comment="Notification when no dictionaries can be installed">Kein neues Wörterbuch zu installieren</string>
<!-- Context: KMP Package strings -->
<string name="no_targets_to_install" comment="Notification when a package doesn't contain keyboards or dictionaries">Keine Tastaturen oder Wörterbücher zu installieren</string>
<!-- Context: KMP Package strings -->
<string name="no_associated_languages" comment="Notification when a keyboard package doesn't contain languages">Tastaturpaket enthält keine zugehörigen Sprachen, die installiert werden könnten</string>
<!-- Context: KMP Package strings -->
<string name="invalid_metadata" comment="kmp.json metadata file is invalid or missing in package">Ungültige Metadaten im Paket</string>
</resources>

View file

@ -95,6 +95,9 @@
<!-- Context: Keyman Settings menu -->
<string name="install_keyboard_or_dictionary" comment="Menu item to install keyboard or dictionary">Install Keyboard or Dictionary</string>
<!-- Context: Keyman Settings menu -->
<string name="change_display_language" comment="Menu action to change interface language">Change display language</string>
<!-- Context: Keyman Settings menu -->
<string name="show_banner" comment="text suggestions banner">Always show banner</string>

View file

@ -66,6 +66,7 @@ dependencies {
implementation 'com.google.android.material:material:1.2.1'
implementation 'commons-io:commons-io:2.6'
implementation 'io.sentry:sentry-android:3.1.0'
implementation 'androidx.preference:preference:1.1.1'
// Robolectric
testImplementation 'androidx.test:core:1.2.0'

View file

@ -0,0 +1,43 @@
/**
* Copyright (C) 2021 SIL International. All rights reserved.
*/
package com.tavultesoft.kmea;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.LocaleList;
import androidx.appcompat.app.AppCompatActivity;
import androidx.preference.PreferenceManager;
import com.tavultesoft.kmea.util.ContextUtils;
import java.util.Locale;
public class BaseActivity extends AppCompatActivity {
@Override
protected void attachBaseContext(Context newBase) {
// Override the app locale using the BCP 47 tag from shared preferences
// Using PreferenceManager because this is before onCreate()
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(newBase);
String languageTag = prefs.getString(DisplayLanguages.displayLanguageKey, DisplayLanguages.unspecifiedLocale);
Locale localeToSwitchTo;
if (languageTag == null || languageTag.equals(DisplayLanguages.unspecifiedLocale)) {
// If display language not specified, use the default locale
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
super.attachBaseContext(newBase);
return;
}
localeToSwitchTo = LocaleList.getDefault().get(0);
} else {
localeToSwitchTo = Locale.forLanguageTag(languageTag);
}
ContextWrapper localeUpdatedContext = ContextUtils.updateLocale(newBase, localeToSwitchTo);
super.attachBaseContext(localeUpdatedContext);
}
}

View file

@ -0,0 +1,40 @@
/**
* Copyright (C) 2021 SIL International. All rights reserved.
*/
package com.tavultesoft.kmea;
public class DisplayLanguages {
private static final String TAG = "DisplayLanguages";
public static final String displayLanguageKey = "DisplayLanguage";
public static final String unspecifiedLocale = ""; // empty string for app to use device's default locale
public static class DisplayLanguageType {
String languageTag;
String languageName;
public DisplayLanguageType(String languageTag, String languageName) {
this.languageTag = languageTag;
this.languageName = languageName;
}
public String getLanguageTag() {
return this.languageTag;
}
public String getLanguageName() {
return this.languageName;
}
}
// Display Language types (as named in translate.keyman.com)
// Order doesn't matter since we're storing BCP-47 tags in the settings preference
public static final DisplayLanguageType[] DisplayLanguages =
{
new DisplayLanguageType(unspecifiedLocale, "Default Locale"),
new DisplayLanguageType("de-DE", "German"),
new DisplayLanguageType("km-KH", "Khmer"),
new DisplayLanguageType("ann", "Obolo")
};
}

View file

@ -22,7 +22,7 @@ import java.util.List;
import static com.tavultesoft.kmea.ConfirmDialogFragment.DialogType.DIALOG_TYPE_DOWNLOAD_KEYBOARD;
import static com.tavultesoft.kmea.ConfirmDialogFragment.DialogType.DIALOG_TYPE_DOWNLOAD_MODEL;
public class KMKeyboardDownloaderActivity extends AppCompatActivity {
public class KMKeyboardDownloaderActivity extends BaseActivity {
// Bundle Keys
// Cloud

View file

@ -420,21 +420,19 @@ public final class KMManager {
* @param keyboard KeyboardType
* @return RelativeLayout.LayoutParams
*/
private static RelativeLayout.LayoutParams getKeyboardLayoutParams(KeyboardType keyboard) {
private static RelativeLayout.LayoutParams getKeyboardLayoutParams() {
int bannerHeight = getBannerHeight(appContext);
int kbHeight = getKeyboardHeight(appContext);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT, bannerHeight + kbHeight);
if (keyboard == KeyboardType.KEYBOARD_TYPE_INAPP) {
params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
}
params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
return params;
}
private static void initInAppKeyboard(Context appContext) {
if (InAppKeyboard == null) {
InAppKeyboard = new KMKeyboard(appContext, KeyboardType.KEYBOARD_TYPE_INAPP);
RelativeLayout.LayoutParams params = getKeyboardLayoutParams(KeyboardType.KEYBOARD_TYPE_INAPP);
RelativeLayout.LayoutParams params = getKeyboardLayoutParams();
InAppKeyboard.setLayoutParams(params);
InAppKeyboard.setVerticalScrollBarEnabled(false);
InAppKeyboard.setHorizontalScrollBarEnabled(false);
@ -447,7 +445,7 @@ public final class KMManager {
private static void initSystemKeyboard(Context appContext) {
if (SystemKeyboard == null) {
SystemKeyboard = new KMKeyboard(appContext, KeyboardType.KEYBOARD_TYPE_SYSTEM);
RelativeLayout.LayoutParams params = getKeyboardLayoutParams(KeyboardType.KEYBOARD_TYPE_SYSTEM);
RelativeLayout.LayoutParams params = getKeyboardLayoutParams();
SystemKeyboard.setLayoutParams(params);
SystemKeyboard.setVerticalScrollBarEnabled(false);
SystemKeyboard.setHorizontalScrollBarEnabled(false);
@ -537,12 +535,12 @@ public final class KMManager {
public static void onConfigurationChanged(Configuration newConfig) {
// KMKeyboard
if (InAppKeyboard != null) {
RelativeLayout.LayoutParams params = getKeyboardLayoutParams(KeyboardType.KEYBOARD_TYPE_INAPP);
RelativeLayout.LayoutParams params = getKeyboardLayoutParams();
InAppKeyboard.setLayoutParams(params);
InAppKeyboard.onConfigurationChanged(newConfig);
}
if (SystemKeyboard != null) {
RelativeLayout.LayoutParams params = getKeyboardLayoutParams(KeyboardType.KEYBOARD_TYPE_SYSTEM);
RelativeLayout.LayoutParams params = getKeyboardLayoutParams();
SystemKeyboard.setLayoutParams(params);
SystemKeyboard.onConfigurationChanged(newConfig);
}
@ -723,7 +721,7 @@ public final class KMManager {
*
* At some point, we might migrate /cloud/ keyboards into packages
* 5) For now, delete cloud/sil_euro_latin*.js
*
*
* Assumption: No legacy keyboards exist in /packages/*.js
* @param context
*/
@ -1061,12 +1059,12 @@ public final class KMManager {
RelativeLayout.LayoutParams params;
if (InAppKeyboard != null && InAppKeyboardLoaded && !InAppKeyboardShouldIgnoreTextChange) {
params = getKeyboardLayoutParams(KeyboardType.KEYBOARD_TYPE_INAPP);
params = getKeyboardLayoutParams();
InAppKeyboard.setLayoutParams(params);
InAppKeyboard.loadJavascript(String.format("enableSuggestions(%s, %s, %s)", model, mayPredict, mayCorrect));
}
if (SystemKeyboard != null && SystemKeyboardLoaded && !SystemKeyboardShouldIgnoreTextChange) {
params = getKeyboardLayoutParams(KeyboardType.KEYBOARD_TYPE_SYSTEM);
params = getKeyboardLayoutParams();
SystemKeyboard.setLayoutParams(params);
SystemKeyboard.loadJavascript(String.format("enableSuggestions(%s, %s, %s)", model, mayPredict, mayCorrect));
}
@ -1278,9 +1276,9 @@ public final class KMManager {
currentBanner = KMManager.KM_BANNER_STATE_BLANK;
if(result1)
InAppKeyboard.setLayoutParams(getKeyboardLayoutParams(KeyboardType.KEYBOARD_TYPE_INAPP));
InAppKeyboard.setLayoutParams(getKeyboardLayoutParams());
if(result2)
SystemKeyboard.setLayoutParams(getKeyboardLayoutParams(KeyboardType.KEYBOARD_TYPE_SYSTEM));
SystemKeyboard.setLayoutParams(getKeyboardLayoutParams());
}
registerAssociatedLexicalModel(languageID);
@ -2005,7 +2003,7 @@ public final class KMManager {
}
currentBanner = (isModelActive && modelPredictionPref) ?
KM_BANNER_STATE_SUGGESTION : KM_BANNER_STATE_BLANK;
RelativeLayout.LayoutParams params = getKeyboardLayoutParams(KeyboardType.KEYBOARD_TYPE_INAPP);
RelativeLayout.LayoutParams params = getKeyboardLayoutParams();
InAppKeyboard.setLayoutParams(params);
} else if (url.indexOf("suggestPopup") >= 0) {
// URL has actual path to the keyboard.html file as a prefix! We need to replace
@ -2273,7 +2271,7 @@ public final class KMManager {
}
currentBanner = (isModelActive && modelPredictionPref) ?
KM_BANNER_STATE_SUGGESTION : KM_BANNER_STATE_BLANK;
RelativeLayout.LayoutParams params = getKeyboardLayoutParams(KeyboardType.KEYBOARD_TYPE_SYSTEM);
RelativeLayout.LayoutParams params = getKeyboardLayoutParams();
SystemKeyboard.setLayoutParams(params);
} else if (url.indexOf("suggestPopup") >= 0) {
// URL has actual path to the keyboard.html file as a prefix! We need to replace

View file

@ -36,7 +36,7 @@ import com.tavultesoft.kmea.util.MapCompat;
import com.tavultesoft.kmea.util.QRCodeUtil;
// Public access is necessary to avoid IllegalAccessException
public final class KeyboardInfoActivity extends AppCompatActivity {
public final class KeyboardInfoActivity extends BaseActivity {
private static final String TAG = "KeyboardInfoActivity";
private static Toolbar toolbar = null;

View file

@ -45,7 +45,7 @@ import android.widget.Toast;
import androidx.appcompat.widget.Toolbar;
public final class KeyboardPickerActivity extends AppCompatActivity {
public final class KeyboardPickerActivity extends BaseActivity {
//TODO: view instances should not be static
private static Toolbar toolbar = null;

View file

@ -38,7 +38,7 @@ import com.tavultesoft.kmea.util.MapCompat;
import static com.tavultesoft.kmea.ConfirmDialogFragment.DialogType.DIALOG_TYPE_DELETE_MODEL;
// Public access is necessary to avoid IllegalAccessException
public final class ModelInfoActivity extends AppCompatActivity {
public final class ModelInfoActivity extends BaseActivity {
private static ArrayList<HashMap<String, String>> infoList = null;
private final String titleKey = "title";

View file

@ -40,7 +40,7 @@ import java.util.Map;
* Gets the list of installable lexical models from Keyman cloud and allows user to download a model.
* Displays a list of available models for a language ID.
*/
public final class ModelPickerActivity extends AppCompatActivity {
public final class ModelPickerActivity extends BaseActivity {
private Context context;
private static Toolbar toolbar = null;

View file

@ -0,0 +1,48 @@
/**
* Copyright (C) 2021 SIL International. All rights reserved.
*/
package com.tavultesoft.kmea.util;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.os.LocaleList;
import java.util.Locale;
/**
* Class to programmatically change the application locale (#4236)
* References
* Tutorial from https://lokalise.com/blog/android-app-localization/#Change_Application_Locale_Programmatically
* https://github.com/nirm2009/lokalise/tree/master/android-i18n
* https://stackoverflow.com/questions/39705739/android-n-change-language-programmatically
*/
public class ContextUtils extends ContextWrapper {
public ContextUtils(Context base) {
super(base);
}
public static ContextWrapper updateLocale(Context context, Locale localeToSwitchTo) {
Resources resources = context.getResources();
Configuration configuration = resources.getConfiguration();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
LocaleList localeList = new LocaleList(localeToSwitchTo);
LocaleList.setDefault(localeList);
configuration.setLocales(localeList);
} else {
configuration.locale = localeToSwitchTo;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
context = context.createConfigurationContext(configuration);
} else {
resources.updateConfiguration(configuration, resources.getDisplayMetrics());
}
return new ContextUtils(context);
}
}

View file

@ -35,6 +35,9 @@ public final class KMPLink {
KMP_STAGING_HOST);
private static final Pattern downloadPattern = Pattern.compile(downloadPatternFormatStr);
private static final String LEGACY_DOWNLOAD_KEYBOARDS_PATTERN_FORMATSTR = "^keyman://localhost/open\\?(.+)?$";
private static final Pattern legacyDownloadPattern = Pattern.compile(LEGACY_DOWNLOAD_KEYBOARDS_PATTERN_FORMATSTR);
// Keyman 14.0+ generated URL for keyboard download links
private static final String KMP_DOWNLOAD_KEYBOARDS_FORMATSTR = "https://%s/go/package/download/%s";
@ -74,6 +77,29 @@ public final class KMPLink {
return status;
}
/**
* Check if a URL is a valid legacy Keyman keyboard download link (uses keyman://)
* @param url String of the URL to parse
* @return boolean
*/
public static boolean isLegacyKeymanDownloadLink(String url) {
boolean status = false;
if (url == null || url.isEmpty()) {
return status;
}
Matcher legacyMatcher = legacyDownloadPattern.matcher(url);
if (legacyMatcher.matches() && legacyMatcher.group(1) != null) {
Uri data = Uri.parse(url);
String keyboard = data.getQueryParameter("keyboard");
String language = data.getQueryParameter("language");
if (keyboard != null && !keyboard.isEmpty()) {
status = true;
}
}
return status;
}
/**
* Get the keyman.com host (production vs staging) based on the tier
* @return String of keyman.com host
@ -130,4 +156,45 @@ public final class KMPLink {
return uri;
}
/**
* Parses a URL and generates the Keyman keyboard download link. URL contains:
* keyboard (required)
* language (optional)
* @param url String of the URL to parse
* @return Uri of the Keyman keyboard download link
*/
public static Uri getLegacyKeyboardDownloadLink(String url) {
Uri uri = null;
if (url == null || url.isEmpty()) {
return uri;
}
Matcher matcher = legacyDownloadPattern.matcher(url);
// Validate deep link with package ID and optional bcp47 tag
if (matcher.matches() && matcher.group(1) != null) {
Uri installUri = Uri.parse(url);
String tier = KMManager.getTier(BuildConfig.VERSION_NAME).toString().toLowerCase();
String host = (tier.equals("alpha") || tier.equals("beta")) ? KMP_STAGING_HOST : KMP_PRODUCTION_HOST;
String keyboardID = installUri.getQueryParameter("keyboard");
String languageID = installUri.getQueryParameter("language");
String downloadURL = String.format(KMP_DOWNLOAD_KEYBOARDS_FORMATSTR,
host,
keyboardID); // Using keyboardID instead of packageID
uri = Uri.parse(downloadURL)
.buildUpon()
.appendQueryParameter(KMKeyboardDownloaderActivity.KMKey_Platform, "android")
.appendQueryParameter(KMKeyboardDownloaderActivity.KMKey_Tier, tier)
.build();
if (languageID != null) {
uri = uri.buildUpon()
.appendQueryParameter(KMKeyboardDownloaderActivity.KMKey_BCP47, languageID)
.build();
}
}
return uri;
}
}

View file

@ -1,3 +1,7 @@
/**
* Copyright (C) 2021 SIL International. All rights reserved.
*/
package com.tavultesoft.kmea.util;
import java.io.BufferedInputStream;
@ -9,11 +13,9 @@ import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* Created by joshua on 12/11/2017.
*/
public class ZipUtils {
private final static String TAG = "ZipUtils";
// Credit to zapi's answer at https://stackoverflow.com/questions/3382996/how-to-unzip-files-programmatically-in-android.
public static void unzip(File zipFile, File targetDirectory) throws IOException {
ZipInputStream zis = new ZipInputStream(
@ -24,6 +26,15 @@ public class ZipUtils {
byte[] buffer = new byte[8192];
while ((ze = zis.getNextEntry()) != null) {
File file = new File(targetDirectory, ze.getName());
// Check for zip path traversal vulnerability
// https://support.google.com/faqs/answer/9294009
String canonicalPath = file.getCanonicalPath();
if (!canonicalPath.startsWith(targetDirectory.getCanonicalPath())) {
// Security exception
throw new SecurityException("Zip traversal error");
}
File dir = ze.isDirectory() ? file : file.getParentFile();
if (!dir.isDirectory() && !dir.mkdirs())
throw new FileNotFoundException("Failed to ensure directory: " +

View file

@ -0,0 +1,131 @@
<?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="one">Uwot-nge</item>
<item quantity="other">Ebi kè uwot-nge</item>
</plurals>
<!-- Context: Title for list -->
<string name="title_add_keyboard" comment="Add a new keyboard">Tap aya uwot-nge gbaalek</string>
<!-- Context: Title for list -->
<string name="title_languages_settings" comment="List of installed languages">Ebi kè usem ewopbe</string>
<!-- Context: Title for list -->
<string name="title_language_settings" comment="Keyman Settings for a language">%1$s Onineen̄</string>
<!-- Context: Button label -->
<string name="label_add" comment="Button to add an item">Tap sọkọ</string>
<!-- Context: Button label -->
<string name="label_back" comment="Button to go back">Gwu kom</string>
<!-- Context: Button label -->
<string name="label_cancel" comment="Button to cancel an action">Gwak</string>
<!-- Context: Button label -->
<string name="label_close" comment="Close a dialog">Chit</string>
<!-- Context: Button label -->
<string name="label_close_keyman" comment="Close the Keyman application">Chit Keyman</string>
<!-- Context: Button label -->
<string name="label_download" comment="Button to confirm downloading an item">Bọkọ tap</string>
<!-- Context: Button label -->
<string name="label_install" comment="Confirmation to install a package">Wop</string>
<!-- Context: Button label -->
<string name="label_later" comment="Do an action at a later time">Ofifi mgbọ</string>
<!-- Context: Button label -->
<string name="label_next" comment="Go to the next screen">Eyi ògọọk</string>
<!-- Context: Button label -->
<string name="label_ok" comment="Button to acknowledge a dialog">ÎSO</string>
<!-- Context: Button label -->
<string name="label_update" comment="Dialog button to update a resource">Bọkọ aya</string>
<!-- Context: Keyboard Updates -->
<string name="cannot_connect" comment="Error message when network connection fails">\nKpekọt itibi itet akpatan̄ Keyman!\n</string>
<!-- Context: Keyboard Updates -->
<string name="confirm_delete_keyboard" comment="Confirmation to delete a keyboard">Ìre oweek ichọchọk uwot-nge yi isan̄a?</string>
<!-- Context: Keyboard Updates -->
<string name="confirm_download_keyboard" comment="Confirmation to download a keyboard update">Ìre oweek ibọbọkọ aya òsolek uwot-nge yi itap?</string>
<!-- Context: Keyboard Updates -->
<string name="confirm_update" comment="Confirmation to update several resources">Ìre owu môma inwenwene uwot-nge mè ikpa-usem itap me eyi ayaya mgbọ keyi?</string>
<!-- Context: Keyboard Updates -->
<string name="keyboard_updates_channel">Irọrọ inu-ikwaan̄ iso usen</string>
<!-- Context: Keyboard Updates -->
<string name="keyboard_updates_available" comment="Notification when resource updates are available">Aya inu-ikwaan̄ ọmọ wa</string>
<!-- Context: Keyboard Updates -->
<string name="update_available" comment="Currently installed version of a resource with an available update">%1$s (Aya Ọmọwa)</string>
<!-- Context: Keyboard Updates -->
<string name="keyboard_update_message" comment="language name: keyboard name"> Aya Uwot-nge Ọmọwa %1$s: %2$s </string>
<!-- Context: Keyboard Updates -->
<string name="dictionary_update_message">Aya Ikpa-usem Ọmọwa %1$s: %2$s </string>
<!-- Context: Keyboard Info and Keyboard Settings -->
<string name="keyboard_version" comment="Label for keyboard version">Òsolek uwot-nge</string>
<!-- Context: Keyboard Info and Keyboard Settings -->
<string name="help_link" comment="Label for help link">Ugọbọ ntap-ubọk</string>
<!-- Context: Keyboard Info and Keyboard Settings -->
<string name="uninstall_keyboard" comment="Label for uninstalling keyboard">Jibi uwot-nge</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">[aya] %1$s</string>
<!-- Context: Keyboard Info and Keyboard Settings -->
<string name="keyboard_qr_code" comment="QR Code description"> Gwuku iman̄ yi kè òbọkọ\nuwot-nge yi me ofifi okwukwut</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Wèek ikpa-usem òje me lek bọkọ tap</string>
<!-- Context: Model Updates -->
<string name="confirm_download_model" comment="Confirmation to download a dictionary update">Ìre oweek ibọbọkọ aya òsolek ikpa-usem yi itap?</string>
<!-- Context: No associated dictionary found -->
<string name="no_associated_model" comment="Confirmation there's no available dictionary to download">Kpunu ikpa-usem mêbọkọbe itap</string>
<!-- Context: Model Updates -->
<string name="catalog_unavailable" comment="Notification that the cloud resource catalog can't be updated">Kpunu onineen̄ inu-ikwaan̄</string>
<!-- Context: Background download messages-->
<string name="catalog_download_start_in_background" comment="Notification that the resource catalog update will begin">Mîbene ikirọ inu-ikwaan̄ iso usen me udun̄-oyet</string>
<!-- Context: Background download messages-->
<string name="catalog_download_is_running_in_background" comment="Notification that the resource catalog update is still downloading">Mêkibọkọ onineen̄ inu-ikwaan̄ itap; soso yaka sa me mgbidim mgbọ!</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 ">Mêkiweek inu-ikwaan̄</string>
<!-- Context: Background download messages-->
<string name="keyboard_download_start_in_background" comment="Notification that a keyboard download has started">Ibọbọkọ uwot-nge itap îbene me Udun̄</string>
<!-- Context: Background download messages-->
<string name="keyboard_download_is_running_in_background" comment="Notification that a keyboard download is still running">Mîwuuk lek ibene ikibọkọ uwot-nge egobobe ya itap; soso neke sa lek me mgbidim mgbọ!</string>
<!-- Context: Background download messages-->
<string name="keyboard_download_finished" comment="Notification that a keyboard download has finished">Mîbọkọ uwot-nge itap inan̄a!</string>
<!-- Context: Background download messages-->
<string name="dictionary_download_start_in_background" comment="Notification that a dictionary download has started">Ibọbọkọ ikpa-usem itap îbene me Udun̄</string>
<!-- Context: Background download messages-->
<string name="dictionary_download_is_running_in_background" comment="Notification that a dictionary download is still running">Mîwuuk lek ibene ikibọkọ ikpa-usem egobobe ya itap; soso neke sa lek me mgbidim mgbọ!</string>
<!-- Context: Background download messages-->
<string name="dictionary_download_finished" comment="Notification that a dictionary download has finished">Mîbọkọ ikpa-usem itap inan̄a.</string>
<!-- Context: General Updates -->
<string name="update_check_unavailable" comment="Error message when a Keyman server can't be reached">Kpekọt ibọkọ etip me akpatan̄!</string>
<!-- Context: General Updates -->
<string name="update_check_current" comment="Notification that all resources are up to date">"Otutuuk inu-ikwaan̄ mîso usen!"</string>
<!-- Context: Model Info -->
<string name="model_version" comment="Title for a dictionary version">Òsolek Ikpa-usem</string>
<!-- Context: Model Info -->
<string name="uninstall_model" comment="Label to uninstall a dictionary">Jibi ikpa-usem</string>
<!-- Context: Model Info -->
<string name="confirm_delete_model" comment="Confirmation to delete a dictionary">Ìre oweek ichọchọk ikpa-usem yi isan̄a?</string>
<!-- Context: Language Settings Keyboard install strings -->
<string name="keyboard_install_toast" comment="Notification when a keyboard is installed">Mîbọkọ uwot-nge %1$s itap</string>
<!-- Context: Language Settings menu strings -->
<string name="enable_corrections" comment="Enable corrections from the suggestion banner">Rọ ikinen̄e ige</string>
<!-- Context: Language Settings menu strings -->
<string name="enable_predictions" comment="Enable predictions from the suggestion banner">Rọ ikibak ikọ onu-misi</string>
<!-- Context: Language Settings menu strings -->
<string name="model_label">Ikpa-usem</string>
<!-- Context: Language Settings menu substring - Check for available dictionary -->
<string name="check_available_model" comment="Check for available dictionary">Kpọ ikpa-usem òkukup</string>
<!-- Context: Language Settings menu strings -->
<string name="model_info_header" comment="Dictionary name">Ikpa-usem: %1$s</string>
<!-- Context: Language Settings menu strings -->
<string name="model_picker_header" comment="Dictionaries for a specified language">Ikpa-usem %1$s</string>
<!-- Context: Language Settings menu strings -->
<!-- Context: Language Settings menu strings -->
<string name="model_install_toast" comment="Notification when a dictionary is installed">Ikpa-usem ebọkọbe itap</string>
<!-- Context: Language Settings menu strings -->
<plurals name="keyboard_count">
<item quantity="one">(uwot-nge %1$d)</item>
<item quantity="other">(Ebi kè uwot-nge %1$d)</item>
</plurals>
<!-- Context: Content descriptions -->
<!-- Context: Content descriptions -->
<!-- Context: Popup menu labels -->
<string name="label_delete" comment="Confirmation to delete an item">Chọk</string>
<!-- Context: Shared preferences name -->
<!-- Context: Other strings -->
<string name="help_bubble_text">Chak ubọk me ere keyi sa nwene uwot-nge</string>
</resources>

View file

@ -0,0 +1,131 @@
<?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="one">Tastatur</item>
<item quantity="other">Tastaturen</item>
</plurals>
<!-- Context: Title for list -->
<string name="title_add_keyboard" comment="Add a new keyboard">Neue Tastatur hinzufügen</string>
<!-- Context: Title for list -->
<string name="title_languages_settings" comment="List of installed languages">Installierte Sprachen</string>
<!-- Context: Title for list -->
<string name="title_language_settings" comment="Keyman Settings for a language">%1$s Einstellungen</string>
<!-- Context: Button label -->
<string name="label_add" comment="Button to add an item">Hinzufügen</string>
<!-- Context: Button label -->
<string name="label_back" comment="Button to go back">Zurück</string>
<!-- Context: Button label -->
<string name="label_cancel" comment="Button to cancel an action">Abbrechen</string>
<!-- Context: Button label -->
<string name="label_close" comment="Close a dialog">Schließen</string>
<!-- Context: Button label -->
<string name="label_close_keyman" comment="Close the Keyman application">Keyman schließen</string>
<!-- Context: Button label -->
<string name="label_download" comment="Button to confirm downloading an item">Herunterladen</string>
<!-- Context: Button label -->
<string name="label_install" comment="Confirmation to install a package">Installieren</string>
<!-- Context: Button label -->
<string name="label_later" comment="Do an action at a later time">Später</string>
<!-- Context: Button label -->
<string name="label_next" comment="Go to the next screen">Weiter</string>
<!-- Context: Button label -->
<string name="label_ok" comment="Button to acknowledge a dialog">Ok</string>
<!-- Context: Button label -->
<string name="label_update" comment="Dialog button to update a resource">Aktualisieren</string>
<!-- Context: Keyboard Updates -->
<string name="cannot_connect" comment="Error message when network connection fails">\nKeine Verbindung zum Keyman Server!\n</string>
<!-- Context: Keyboard Updates -->
<string name="confirm_delete_keyboard" comment="Confirmation to delete a keyboard">Möchten Sie diese Tastatur löschen?</string>
<!-- Context: Keyboard Updates -->
<string name="confirm_download_keyboard" comment="Confirmation to download a keyboard update">Möchten Sie die neueste Version dieser Tastatur herunterladen?</string>
<!-- Context: Keyboard Updates -->
<string name="confirm_update" comment="Confirmation to update several resources">Möchten Sie Tastaturen und Wörterbücher jetzt aktualisieren?</string>
<!-- Context: Keyboard Updates -->
<string name="keyboard_updates_channel">Ressourcen Updates</string>
<!-- Context: Keyboard Updates -->
<string name="keyboard_updates_available" comment="Notification when resource updates are available">Ressourcen Updates verfügbar</string>
<!-- Context: Keyboard Updates -->
<string name="update_available" comment="Currently installed version of a resource with an available update">%1$s (Update verfügbar)</string>
<!-- Context: Keyboard Updates -->
<string name="keyboard_update_message" comment="language name: keyboard name"> Updates für Tastatur %1$s verfügbar: %2$s </string>
<!-- Context: Keyboard Updates -->
<string name="dictionary_update_message">Updates für Wörterbuch %1$s verfügbar: %2$s </string>
<!-- Context: Keyboard Info and Keyboard Settings -->
<string name="keyboard_version" comment="Label for keyboard version">Tastaturversion</string>
<!-- Context: Keyboard Info and Keyboard Settings -->
<string name="help_link" comment="Label for help link">Hilfe-Link</string>
<!-- Context: Keyboard Info and Keyboard Settings -->
<string name="uninstall_keyboard" comment="Label for uninstalling keyboard">Tastatur deinstallieren</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">[neu] %1$s</string>
<!-- Context: Keyboard Info and Keyboard Settings -->
<string name="keyboard_qr_code" comment="QR Code description"> Scannen Sie diesen Code, um diese\nTastatur auf einem anderen Gerät zu laden</string>
<!-- Context: Query for associated dictionary -->
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Suche nach zugehörigem Wörterbuch zum Herunterladen</string>
<!-- Context: Model Updates -->
<string name="confirm_download_model" comment="Confirmation to download a dictionary update">Möchten Sie die neueste Version dieses Wörterbuchs herunterladen?</string>
<!-- Context: No associated dictionary found -->
<string name="no_associated_model" comment="Confirmation there's no available dictionary to download">Kein Wörterbuch zum Herunterladen</string>
<!-- Context: Model Updates -->
<string name="catalog_unavailable" comment="Notification that the cloud resource catalog can't be updated">Der Ressourcenkatalog ist nicht verfügbar</string>
<!-- Context: Background download messages-->
<string name="catalog_download_start_in_background" comment="Notification that the resource catalog update will begin">Katalogaktualisierung im Hintergrund gestartet</string>
<!-- Context: Background download messages-->
<string name="catalog_download_is_running_in_background" comment="Notification that the resource catalog update is still downloading"> Der Katalog wird noch heruntergeladen. Bitte versuchen Sie es gleich nochmal!</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 ">Suche nach Ressource</string>
<!-- Context: Background download messages-->
<string name="keyboard_download_start_in_background" comment="Notification that a keyboard download has started">Das Herunterladen der Tastatur wurde im Hintergrund gestartet</string>
<!-- Context: Background download messages-->
<string name="keyboard_download_is_running_in_background" comment="Notification that a keyboard download is still running">Die ausgewählte Tastatur wird bereits heruntergeladen. Bitte versuchen Sie es gleich nochmal!</string>
<!-- Context: Background download messages-->
<string name="keyboard_download_finished" comment="Notification that a keyboard download has finished">Download der Tastatur ist abgeschlossen!</string>
<!-- Context: Background download messages-->
<string name="dictionary_download_start_in_background" comment="Notification that a dictionary download has started">Das Herunterladen des Wörterbuchs wurde im Hintergrund gestartet</string>
<!-- Context: Background download messages-->
<string name="dictionary_download_is_running_in_background" comment="Notification that a dictionary download is still running">Das ausgewählte Wörterbuch wird bereits heruntergeladen. Bitte versuchen Sie es gleich nochmal!</string>
<!-- Context: Background download messages-->
<string name="dictionary_download_finished" comment="Notification that a dictionary download has finished">Download des Wörterbuchs ist abgeschlossen.</string>
<!-- Context: General Updates -->
<string name="update_check_unavailable" comment="Error message when a Keyman server can't be reached">Fehler beim Zugriff auf den Server!</string>
<!-- Context: General Updates -->
<string name="update_check_current" comment="Notification that all resources are up to date">"Alle Ressourcen sind auf dem neuesten Stand!"</string>
<!-- Context: Model Info -->
<string name="model_version" comment="Title for a dictionary version">Wörterbuchversion</string>
<!-- Context: Model Info -->
<string name="uninstall_model" comment="Label to uninstall a dictionary">Wörterbuch deinstallieren</string>
<!-- Context: Model Info -->
<string name="confirm_delete_model" comment="Confirmation to delete a dictionary">Möchten Sie dieses Wörterbuch löschen?</string>
<!-- Context: Language Settings Keyboard install strings -->
<string name="keyboard_install_toast" comment="Notification when a keyboard is installed">%1$s -Tastatur installiert</string>
<!-- Context: Language Settings menu strings -->
<string name="enable_corrections" comment="Enable corrections from the suggestion banner">Korrekturen aktivieren</string>
<!-- Context: Language Settings menu strings -->
<string name="enable_predictions" comment="Enable predictions from the suggestion banner">Vorhersagen aktivieren</string>
<!-- Context: Language Settings menu strings -->
<string name="model_label">Wörterbuch</string>
<!-- Context: Language Settings menu substring - Check for available dictionary -->
<string name="check_available_model" comment="Check for available dictionary">Auf verfügbares Wörterbuch prüfen</string>
<!-- Context: Language Settings menu strings -->
<string name="model_info_header" comment="Dictionary name">Wörterbuch: %1$s</string>
<!-- Context: Language Settings menu strings -->
<string name="model_picker_header" comment="Dictionaries for a specified language">%1$s Wörterbücher</string>
<!-- Context: Language Settings menu strings -->
<!-- Context: Language Settings menu strings -->
<string name="model_install_toast" comment="Notification when a dictionary is installed">Wörterbuch installiert</string>
<!-- Context: Language Settings menu strings -->
<plurals name="keyboard_count">
<item quantity="one">(%1$d Tastatur)</item>
<item quantity="other">(%1$d Tastaturen)</item>
</plurals>
<!-- Context: Content descriptions -->
<!-- Context: Content descriptions -->
<!-- Context: Popup menu labels -->
<string name="label_delete" comment="Confirmation to delete an item">Entfernen</string>
<!-- Context: Shared preferences name -->
<!-- Context: Other strings -->
<string name="help_bubble_text">Hier tippen, um Tastatur zu ändern</string>
</resources>

View file

@ -0,0 +1,39 @@
/**
* Copyright (C) 2021 SIL International. All rights reserved.
*/
package com.tavultesoft.kmea.util;
import com.tavultesoft.kmea.util.ZipUtils;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import java.io.File;
import java.io.IOException;
public class ZipUtilsTest {
private static final File TEST_RESOURCE_ROOT = new File("test_resources");
private static final File TEST_EXTRACTION_ROOT = new File(TEST_RESOURCE_ROOT, "temp");
// Zip file with Windows paths to test zip slip vulnerability
private static final File TEST_ZIP_SLIP_FILE = new File(TEST_RESOURCE_ROOT, "zip-slip-win.zip");
@Rule
public ExpectedException exceptionRule = ExpectedException.none();
@Test
public void test_unzip_throwsSecurityException() throws SecurityException, IOException {
// Zip file for testing zip slip vulnerability has Windows paths
Assume.assumeTrue(System.getProperty("os.name").toLowerCase().startsWith("win"));
exceptionRule.expect(SecurityException.class);
exceptionRule.expectMessage("Zip traversal error");
ZipUtils.unzip(TEST_ZIP_SLIP_FILE, TEST_EXTRACTION_ROOT);
}
}

Binary file not shown.

View file

@ -1,4 +1,7 @@
apply plugin: 'com.android.application'
plugins {
id 'com.android.application'
id 'io.sentry.android.gradle'
}
android {
compileSdkVersion 29
@ -40,5 +43,5 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.3.0-alpha02'
implementation 'com.google.android.material:material:1.2.1'
api(name: 'keyman-engine', ext: 'aar')
implementation 'io.sentry:sentry-android:2.3.0'
implementation 'io.sentry:sentry-android:3.1.0'
}

View file

@ -7,7 +7,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.1'
classpath 'io.sentry:sentry-android:2.0.1'
classpath 'io.sentry:sentry-android-gradle-plugin:1.7.36'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View file

@ -1,4 +1,7 @@
apply plugin: 'com.android.application'
plugins {
id 'com.android.application'
id 'io.sentry.android.gradle'
}
android {
compileSdkVersion 29
@ -39,5 +42,5 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.3.0-alpha02'
implementation 'com.google.android.material:material:1.2.1'
api (name:'keyman-engine', ext:'aar')
implementation 'io.sentry:sentry-android:2.3.0'
implementation 'io.sentry:sentry-android:3.1.0'
}

View file

@ -14,11 +14,13 @@ import java.util.HashMap;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.widget.FrameLayout;
public class SystemKeyboard extends InputMethodService implements OnKeyboardEventListener {
@ -157,6 +159,16 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
KMManager.onConfigurationChanged(newConfig);
}
@Override
public void onConfigureWindow(Window win, boolean isFullscreen, boolean isCandidatesOnly) {
super.onConfigureWindow(win, isFullscreen, isCandidatesOnly);
// We don't currently use isFullscreen or isCandidatesOnly; we always want to MATCH_PARENT,
// unlike the default for height which is WRAP_CONTENT. We then adjust the touchable area
// in `onCalculateInsets`
win.setLayout(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
}
@Override
public void onComputeInsets(InputMethodService.Insets outInsets) {
super.onComputeInsets(outInsets);
@ -173,6 +185,7 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
int bannerHeight = KMManager.getBannerHeight(this);
int kbHeight = KMManager.getKeyboardHeight(this);
outInsets.contentTopInsets = inputViewHeight - bannerHeight - kbHeight;
outInsets.visibleTopInsets = outInsets.contentTopInsets;
outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
outInsets.touchableRegion.set(0, outInsets.contentTopInsets, size.x, size.y);
}

View file

@ -7,7 +7,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.1'
classpath 'io.sentry:sentry-android:2.0.1'
classpath 'io.sentry:sentry-android-gradle-plugin:1.7.36'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View file

@ -1,4 +1,7 @@
apply plugin: 'com.android.application'
plugins {
id 'com.android.application'
id 'io.sentry.android.gradle'
}
ext.rootPath = '../../../'
apply from: "$rootPath/version.gradle"
@ -46,5 +49,5 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.3.0-alpha02'
implementation 'com.google.android.material:material:1.2.1'
api (name:'keyman-engine', ext:'aar')
implementation 'io.sentry:sentry-android:2.3.0'
implementation 'io.sentry:sentry-android:3.1.0'
}

View file

@ -7,7 +7,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.1'
classpath 'io.sentry:sentry-android:2.0.1'
classpath 'io.sentry:sentry-android-gradle-plugin:1.7.36'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View file

@ -20,6 +20,7 @@ KMX_BOOL km::kbp::kmx::g_silent = FALSE;
KMX_Processor::KMX_Processor() : m_actions(&m_context), m_options(&m_keyboard) {
m_indexStack = new KMX_WORD[GLOBAL_ContextStackSize];
m_miniContext = new KMX_WCHAR[GLOBAL_ContextStackSize];
m_miniContextIfLen = 0;
}
KMX_Processor::~KMX_Processor() {
@ -242,6 +243,8 @@ KMX_BOOL KMX_Processor::ProcessGroup(LPGROUP gp, KMX_BOOL *pOutputKeystroke)
assert(kkp != NULL);
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)
u16cpy(m_miniContext, /*GLOBAL_ContextStackSize,*/ m_context.Buf(xstrlen_ignoreifopt(kkp->dpContext)-1) /*, GLOBAL_ContextStackSize*/); // I3162 // I3536
@ -353,18 +356,18 @@ int KMX_Processor::PostString(PKMX_WCHAR str, LPKEYBOARD lpkb, PKMX_WCHAR endstr
m_actions.QueueAction(QIT_BELL, 0);
break;
case CODE_CONTEXT: // copy the context to the output
for(q = m_miniContext; *q; q++) {
m_actions.QueueAction(QIT_CHAR, *q);
}
{
KMX_BOOL ignoreOutputKeystroke;
PostString(m_miniContext, lpkb, (PKMX_WCHAR) u16chr(m_miniContext, 0), &ignoreOutputKeystroke);
break;
}
case CODE_CONTEXTEX:
p++;
for(q = m_miniContext, i = 0; *q && i < *p-1; i++, q=incxstr(q));
if(*q) {
m_actions.QueueAction(QIT_CHAR, *q);
if(Uni_IsSurrogate1(*q) && Uni_IsSurrogate2(*(q+1))) {
m_actions.QueueAction(QIT_CHAR, *(q+1));
}
for (q = m_miniContext, i = m_miniContextIfLen; *q && i < *p - 1; i++, q = incxstr(q));
if (*q) {
KMX_BOOL ignoreOutputKeystroke;
temp = incxstr(q);
PostString(q, lpkb, temp, &ignoreOutputKeystroke);
}
break;
case CODE_RETURN: // stop processing and start PostAllKeys

View file

@ -28,6 +28,7 @@ class KMX_Processor {
private:
PKMX_WORD m_indexStack;
PKMX_WCHAR m_miniContext;
int m_miniContextIfLen; // number of if() statements excluded from start of m_miniContext
KMSTATE m_state;
km_kbp_state *m_kbp_state;

View file

@ -0,0 +1,19 @@
c Description: Tests that context(x) takes if() statements into account. See #4275.
c keys: [K_A][K_B]
c expected: ex
c context:
store(&version) '10.0'
begin Unicode > use(Main)
group(Main) using keys
store(nfc) '0'
store(diaeresisBase) 'ae'
+ 'a' > 'exay'
c Test #1 for if() with context(n)
if(nfc = "0") any(diaeresisBase) 'x' any(diaeresisBase) 'y' + 'b' > context(2) context(3)

View file

@ -0,0 +1,17 @@
c Description: Tests that context emits deadkeys correctly. See #4275.
c keys: [K_Y][K_Z][SHIFT K_SLASH]
c expected: correct
c context:
store(&version) '10.0'
begin Unicode > use(Main)
group(Main) using keys
+ 'y' > dk(1) 'a' dk(2) 'b' dk(3)
c Test #2 for dk(d) and context
dk(1) 'a' dk(2) 'b' dk(3) + 'z' > '<' context '>'
'<' dk(1) 'a' dk(2) 'b' dk(3) '>' + '?' > 'correct'

View file

@ -0,0 +1,17 @@
c Description: Tests that context() emits deadkeys correctly. See #4275.
c keys: [K_Y][SHIFT K_M][SHIFT K_SLASH]
c expected: correct
c context:
store(&version) '10.0'
begin Unicode > use(Main)
group(Main) using keys
+ 'y' > dk(1) 'a' dk(2) 'b' dk(3)
c Test #3 for dk(d) and context(n)
dk(1) 'a' dk(2) 'b' dk(3) + 'M' > '<' context(1) '>'
'<' dk(1) '>' + '?' > 'correct'

View file

@ -58,7 +58,11 @@ tests = [
'040 - long context',
'041 - long context and deadkeys',
'042 - long context and split deadkeys',
'043 - output and keystroke'
'043 - output and keystroke',
'044 - if and context',
'045 - deadkey and context',
'046 - deadkey and contextex'
]
if build_machine.system() == 'windows'

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/input-processor",
"version": "14.0.222",
"version": "14.0.227",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/input-processor",
"version": "14.0.222",
"version": "14.0.227",
"description": "The core text and prediction processing engine for KeymanWeb",
"repository": {
"type": "git",
@ -18,8 +18,8 @@
},
"homepage": "https://github.com/keymanapp/keyman#readme",
"devDependencies": {
"@keymanapp/lexical-model-compiler": "^14.0.222",
"@keymanapp/resources-gosh": "^14.0.222",
"@keymanapp/lexical-model-compiler": "^14.0.227",
"@keymanapp/resources-gosh": "^14.0.227",
"@types/node": "^11.9.4",
"chai": "^4.2.0",
"mocha": "^5.2.0",
@ -34,11 +34,11 @@
"mocha": "mocha"
},
"dependencies": {
"@keymanapp/keyboard-processor": "^14.0.222",
"@keymanapp/lexical-model-layer": "^14.0.222",
"@keymanapp/models-types": "^14.0.222",
"@keymanapp/web-environment": "^14.0.222",
"@keymanapp/web-utils": "^14.0.222",
"@keymanapp/keyboard-processor": "^14.0.227",
"@keymanapp/lexical-model-layer": "^14.0.227",
"@keymanapp/models-types": "^14.0.227",
"@keymanapp/web-environment": "^14.0.227",
"@keymanapp/web-utils": "^14.0.227",
"eventemitter3": "^4.0.0"
}
}

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/keyboard-processor",
"version": "14.0.222",
"version": "14.0.227",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/keyboard-processor",
"version": "14.0.222",
"version": "14.0.227",
"description": "Core module for Keyman keyboard support in KeymanWeb.",
"repository": {
"type": "git",
@ -18,7 +18,7 @@
},
"homepage": "https://github.com/keymanapp/keyman#readme",
"devDependencies": {
"@keymanapp/resources-gosh": "^14.0.222",
"@keymanapp/resources-gosh": "^14.0.227",
"chai": "^4.2.0",
"mocha": "^5.2.0",
"mocha-teamcity-reporter": "^3.0.0",
@ -31,9 +31,9 @@
"mocha": "mocha"
},
"dependencies": {
"@keymanapp/models-types": "^14.0.222",
"@keymanapp/web-environment": "^14.0.222",
"@keymanapp/web-utils": "^14.0.222",
"@keymanapp/models-types": "^14.0.227",
"@keymanapp/web-environment": "^14.0.227",
"@keymanapp/web-utils": "^14.0.227",
"@types/node": "^11.9.4",
"ts-node": "^8.0.2"
}

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/recorder-core",
"version": "14.0.222",
"version": "14.0.227",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/recorder-core",
"version": "14.0.222",
"version": "14.0.227",
"description": "Core classes used to develop KeymanWeb test cases based on keystrokes",
"main": "index.js",
"scripts": {
@ -17,10 +17,10 @@
},
"homepage": "https://github.com/keymanapp/keyman#readme",
"dependencies": {
"@keymanapp/keyboard-processor": "^14.0.222",
"@keymanapp/models-types": "^14.0.222",
"@keymanapp/web-environment": "^14.0.222",
"@keymanapp/web-utils": "^14.0.222"
"@keymanapp/keyboard-processor": "^14.0.227",
"@keymanapp/models-types": "^14.0.227",
"@keymanapp/web-environment": "^14.0.227",
"@keymanapp/web-utils": "^14.0.227"
},
"devDependencies": {
"typescript": "^3.8.3"

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/web-sentry-manager",
"version": "14.0.222",
"version": "14.0.227",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/web-sentry-manager",
"version": "14.0.222",
"version": "14.0.227",
"description": "Common setup for KeymanWeb's Sentry-based error reporting",
"main": "dist/index.js",
"scripts": {
@ -20,7 +20,7 @@
"typescript": "^3.8.3"
},
"dependencies": {
"@keymanapp/web-environment": "^14.0.222",
"@keymanapp/web-environment": "^14.0.227",
"@sentry/browser": "^5.27.4"
}
}

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/web-utils",
"version": "14.0.222",
"version": "14.0.227",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/web-utils",
"version": "14.0.222",
"version": "14.0.227",
"description": "Common utility functions used throughout other Keyman packages",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@ -20,8 +20,8 @@
},
"homepage": "https://github.com/keymanapp/keyman#readme",
"devDependencies": {
"@keymanapp/resources-gosh": "^14.0.222",
"@keymanapp/web-environment": "^14.0.222",
"@keymanapp/resources-gosh": "^14.0.227",
"@keymanapp/web-environment": "^14.0.227",
"@types/node": "^14.0.5",
"typescript": "^3.8.3"
}

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/models-templates",
"version": "14.0.222",
"version": "14.0.227",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/models-templates",
"version": "14.0.222",
"version": "14.0.227",
"description": "Backing model implementations (templates) for Keyman's modelling layer",
"keywords": [
"lm",
@ -42,8 +42,8 @@
"url": "https://github.com/keymanapp/keyman/issues"
},
"devDependencies": {
"@keymanapp/models-types": "^14.0.222",
"@keymanapp/web-utils": "^14.0.222",
"@keymanapp/models-types": "^14.0.227",
"@keymanapp/web-utils": "^14.0.227",
"@types/chai": "^4.2.11",
"@types/mocha": "^7.0.2",
"@types/node": "^14.0.4",
@ -52,6 +52,6 @@
"typescript": "^3.8.3"
},
"dependencies": {
"@keymanapp/models-wordbreakers": "^14.0.222"
"@keymanapp/models-wordbreakers": "^14.0.227"
}
}

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/models-types",
"version": "14.0.222",
"version": "14.0.227",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/models-types",
"version": "14.0.222",
"version": "14.0.227",
"description": "Type definitions in used in the modeling (lexical model/predictive text) component of Keyman.",
"types": "./index.d.ts",
"scripts": {

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/models-wordbreakers",
"version": "14.0.222",
"version": "14.0.227",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/models-wordbreakers",
"version": "14.0.222",
"version": "14.0.227",
"description": "Word breakers bundled in the modeling layer",
"keywords": [
"lexical model",
@ -40,7 +40,7 @@
"url": "https://github.com/keymanapp/keyman/issues"
},
"devDependencies": {
"@keymanapp/models-types": "^14.0.222",
"@keymanapp/models-types": "^14.0.227",
"@types/chai": "^4.2.11",
"@types/mocha": "^7.0.2",
"chai": "^4.2.0",

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/lexical-model-layer",
"version": "14.0.222",
"version": "14.0.227",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/lexical-model-layer",
"version": "14.0.222",
"version": "14.0.227",
"description": "Keyman/Predictive Text integration layer",
"main": "build/index.js",
"scripts": {
@ -21,8 +21,8 @@
},
"homepage": "https://github.com/keymanapp/keyman#readme",
"devDependencies": {
"@keymanapp/models-types": "^14.0.222",
"@keymanapp/resources-gosh": "^14.0.222",
"@keymanapp/models-types": "^14.0.227",
"@keymanapp/resources-gosh": "^14.0.227",
"@types/node": "^10.17.21",
"chai": "^4.2.0",
"karma": "^4.2.0",
@ -46,10 +46,10 @@
"typescript": "^3.8.3"
},
"dependencies": {
"@keymanapp/models-templates": "^14.0.222",
"@keymanapp/models-wordbreakers": "^14.0.222",
"@keymanapp/web-environment": "^14.0.222",
"@keymanapp/web-utils": "^14.0.222",
"@keymanapp/models-templates": "^14.0.227",
"@keymanapp/models-wordbreakers": "^14.0.227",
"@keymanapp/web-environment": "^14.0.227",
"@keymanapp/web-utils": "^14.0.227",
"es6-shim": "^0.35.5",
"string.prototype.codepointat": "^0.2.1",
"string.prototype.startswith": "^0.2.0"

View file

@ -50,11 +50,17 @@ files:
dest: /windows/strings.xml
translation: /windows/src/desktop/kmshell/locale/%locale%/%original_file_name%
type: android
languages_mapping:
locale:
de: de
- source: /windows/src/desktop/setup/locale/en/strings.xml
dest: /windows/setup/strings.xml
translation: /windows/src/desktop/setup/locale/%locale%/%original_file_name%
type: android
languages_mapping:
locale:
de: de
# iOS files

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/lexical-model-compiler",
"version": "14.0.222",
"version": "14.0.227",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/lexical-model-compiler",
"version": "14.0.222",
"version": "14.0.227",
"description": "Keyman Developer lexical model compiler",
"keywords": [
"keyboard",
@ -37,14 +37,14 @@
"node": ">=12.0.0"
},
"dependencies": {
"@keymanapp/models-types": "^14.0.222",
"@keymanapp/models-types": "^14.0.227",
"commander": "^3.0.0",
"typescript": "^3.8.3",
"xml2js": "^0.4.19"
},
"devDependencies": {
"@keymanapp/models-templates": "^14.0.222",
"@keymanapp/models-wordbreakers": "^14.0.222",
"@keymanapp/models-templates": "^14.0.227",
"@keymanapp/models-wordbreakers": "^14.0.227",
"@types/chai": "^4.1.7",
"@types/mocha": "^5.2.7",
"@types/node": "^10.14.6",

View file

@ -0,0 +1,2 @@
/* Class = "UILabel"; text = "Scan this code to load this keyboard on another device"; ObjectID = "z2O-MT-IoV"; */
"z2O-MT-IoV.text" = "Scannen Sie diesen Code, um diese Tastatur auf einem anderen Gerät zu laden";

View file

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>menu-langsettings-lexical-model-count</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@count@</string>
<key>count</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>u</string>
<key>one</key>
<string>%u Wörterbuch installiert</string>
<key>other</key>
<string>%u Wörterbücher installiert</string>
</dict>
</dict>
<key>notification-update-failed</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@count@</string>
<key>count</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>u</string>
<key>one</key>
<string>%u Aktualisierung fehlgeschlagen</string>
<key>other</key>
<string>%u Aktualisierungen fehlgeschlagen</string>
</dict>
</dict>
<key>notification-update-success</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@count@</string>
<key>count</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>u</string>
<key>one</key>
<string>%u Aktualisierung erfolgreich</string>
<key>other</key>
<string>%u Aktualisierungen erfolgreich</string>
</dict>
</dict>
<key>settings-keyboards-installed-count</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@count@</string>
<key>count</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>u</string>
<key>one</key>
<string>%u Tastatur installiert</string>
<key>other</key>
<string>%u Tastaturen installiert</string>
</dict>
</dict>
<key>settings-languages-installed-count</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@count@</string>
<key>count</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>u</string>
<key>one</key>
<string>%u Sprache installiert</string>
<key>other</key>
<string>%u Sprachen installiert</string>
</dict>
</dict>
<key>package-default-found-keyboards</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@count@</string>
<key>count</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>u</string>
<key>one</key>
<string>%u Tastatur im Paket gefunden:</string>
<key>other</key>
<string>%u Tastaturen im Paket gefunden:</string>
</dict>
</dict>
<key>package-default-found-lexical-models</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@count@</string>
<key>count</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>u</string>
<key>one</key>
<string>%u Wörterbuch im Paket gefunden:</string>
<key>other</key>
<string>%u Wörterbücher im Paket gefunden:</string>
</dict>
</dict>
</dict>
</plist>

Binary file not shown.

View file

@ -16,5 +16,5 @@
"lerna",
"typescript"
],
"version": "14.0.222"
"version": "14.0.227"
}

View file

@ -0,0 +1,332 @@
msgid ""
msgstr ""
"Project-Id-Version: keyman\n"
"Report-Msgid-Bugs-To: <support@keyman.com>\n"
"POT-Creation-Date: 2020-08-19 19:17+0200\n"
"PO-Revision-Date: 2021-01-19 08:16\n"
"Last-Translator: \n"
"Language-Team: German\n"
"Language: de_DE\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: keyman\n"
"X-Crowdin-Project-ID: 386703\n"
"X-Crowdin-Language: de\n"
"X-Crowdin-File: /master/linux/keyman-config.pot\n"
"X-Crowdin-File-ID: 504\n"
#: keyman_config/__init__.py:68
msgid "Neither sentry-sdk nor raven is available. Not enabling Sentry error reporting."
msgstr "Weder sentry-sdk noch raven sind verfügbar. Sentry-Fehlerberichte werden nicht aktiviert."
#: keyman_config/downloadkeyboard.py:23
msgid "Download Keyman keyboards"
msgstr "Keyman-Tastaturen herunterladen"
#: keyman_config/downloadkeyboard.py:37 keyman_config/keyboard_details.py:49
#: keyman_config/keyboard_details.py:340 keyman_config/view_installed.py:205
msgid "_Close"
msgstr "_Schließen"
#: keyman_config/install_kmp.py:99
msgid "You do not have permissions to install the keyboard files to the shared area /usr/local/share/keyman"
msgstr "Sie haben keine Berechtigung, die Tastaturdateien in den gemeinsamen Bereich /usr/local/share/keyman zu installieren"
#: keyman_config/install_kmp.py:103
msgid "You do not have permissions to install the documentation to the shared documentation area /usr/local/share/doc/keyman"
msgstr "Sie haben keine Berechtigung, die Dokumentation in den gemeinsamen Dokumentationsbereich /usr/local/share/doc/keyman zu installieren"
#: keyman_config/install_kmp.py:107
msgid "You do not have permissions to install the font files to the shared font area /usr/local/share/fonts"
msgstr "Sie haben keine Berechtigung, die Schriftarten in den gemeinsamen Bereich /usr/local/share/fonts zu installieren"
#: keyman_config/install_kmp.py:179
#, python-brace-format
msgid "install_kmp.py: error: No kmp.json or kmp.inf found in {package}"
msgstr "install_kmp.py: Fehler: Keine kmp.json oder kmp.inf in {package} gefunden"
#: keyman_config/install_kmp.py:246
#, python-brace-format
msgid "install_kmp.py: error: No kmp.json or kmp.inf found in {packageFile}"
msgstr "install_kmp.py: Fehler: Keine kmp.json oder kmp.inf in {packageFile} gefunden"
#: keyman_config/install_window.py:54
#, python-brace-format
msgid "Installing keyboard/package {keyboardid}"
msgstr "Installiere Tastatur/Paket {keyboardid}"
#: keyman_config/install_window.py:72 keyman_config/install_window.py:93
msgid "Keyboard is installed already"
msgstr "Tastatur ist bereits installiert"
#: keyman_config/install_window.py:74
#, python-brace-format
msgid "The {name} keyboard is already installed at version {version}. Do you want to uninstall then reinstall it?"
msgstr "Die {name} -Tastatur ist bereits in Version {version} installiert. Möchten Sie sie deinstallieren und dann neu installieren?"
#: keyman_config/install_window.py:95
#, python-brace-format
msgid "The {name} keyboard is already installed with a newer version {installedversion}. Do you want to uninstall it and install the older version {version}?"
msgstr "Die {name} -Tastatur ist bereits mit einer neueren Version {installedversion} installiert. Möchten Sie sie deinstallieren und dann die ältere Version {version} installieren?"
#: keyman_config/install_window.py:128
msgid "Keyboard layouts: "
msgstr "Tastatur-Layouts: "
#: keyman_config/install_window.py:147
msgid "Fonts: "
msgstr "Schriftarten: "
#: keyman_config/install_window.py:167 keyman_config/keyboard_details.py:96
msgid "Package version: "
msgstr "Paketversion: "
#: keyman_config/install_window.py:179
msgid "Author: "
msgstr "Autor: "
#: keyman_config/install_window.py:197
msgid "Website: "
msgstr "Webseite: "
#: keyman_config/install_window.py:211
msgid "Copyright: "
msgstr "Copyright: "
#: keyman_config/install_window.py:245
msgid "Details"
msgstr "Details"
#: keyman_config/install_window.py:248
msgid "README"
msgstr "LIESMICH"
#: keyman_config/install_window.py:256 keyman_config/view_installed.py:200
msgid "_Install"
msgstr "_Installieren"
#: keyman_config/install_window.py:260
msgid "_Cancel"
msgstr "_Abbrechen"
#: keyman_config/install_window.py:305
#, python-brace-format
msgid "Keyboard {name} installed"
msgstr "Tastatur {name} installiert"
#: keyman_config/install_window.py:310 keyman_config/install_window.py:315
#, python-brace-format
msgid "Keyboard {name} could not be installed."
msgstr "Tastatur {name} konnte nicht installiert werden."
#: keyman_config/install_window.py:311
msgid "Error Message:"
msgstr "Fehlermeldung:"
#: keyman_config/install_window.py:316
msgid "Warning Message:"
msgstr "Warnmeldung:"
#: keyman_config/keyboard_details.py:37
#, python-brace-format
msgid "{name} keyboard"
msgstr "{name}-Tastatur"
#: keyman_config/keyboard_details.py:53
msgid "ERROR: Keyboard metadata is damaged.\n"
"Please \"Uninstall\" and then \"Install\" the keyboard."
msgstr "FEHLER: Die Tastatur-Metadaten sind beschädigt.\n"
"Bitte \"deinstallieren\" Sie die Tastatur und \"Installieren\" dann erneut."
#: keyman_config/keyboard_details.py:74
msgid "Package name: "
msgstr "Paketname: "
#: keyman_config/keyboard_details.py:85
msgid "Package id: "
msgstr "Paket-Id: "
#: keyman_config/keyboard_details.py:108
msgid "Package description: "
msgstr "Paketbeschreibung: "
#: keyman_config/keyboard_details.py:121
msgid "Package author: "
msgstr "Paketautor: "
#: keyman_config/keyboard_details.py:133
msgid "Package copyright: "
msgstr "Copyright des Pakets: "
#: keyman_config/keyboard_details.py:174
msgid "Keyboard filename: "
msgstr "Tastatur-Dateiname: "
#: keyman_config/keyboard_details.py:187
msgid "Keyboard name: "
msgstr "Name der Tastatur: "
#: keyman_config/keyboard_details.py:198
msgid "Keyboard id: "
msgstr "Tastatur-Id: "
#: keyman_config/keyboard_details.py:209
msgid "Keyboard version: "
msgstr "Tastaturversion: "
#: keyman_config/keyboard_details.py:221
msgid "Keyboard author: "
msgstr "Tastatur-Autor: "
#: keyman_config/keyboard_details.py:232
msgid "Keyboard license: "
msgstr "Tastatur-Lizenz: "
#: keyman_config/keyboard_details.py:243
msgid "Keyboard description: "
msgstr "Tastaturbeschreibung: "
#: keyman_config/keyboard_details.py:334
#, python-brace-format
msgid "Scan this code to load this keyboard\n"
"on another device or <a href='{uri}'>share online</a>"
msgstr "Scannen Sie diesen Code, um diese Tastatur\n"
"auf einem anderen Gerät zu laden oder <a href='{uri}'>online</a> zu teilen"
#: keyman_config/options.py:24
#, python-brace-format
msgid "{packageId} Settings"
msgstr "{packageId} Einstellungen"
#: keyman_config/view_installed.py:30
msgid "Keyman Configuration"
msgstr "Keyman-Einstellungen"
#: keyman_config/view_installed.py:60
msgid "Choose a kmp file..."
msgstr "KMP-Datei auswählen..."
#. i18n: file type in file selection dialog
#: keyman_config/view_installed.py:65
msgid "KMP files"
msgstr "KMP-Dateien"
#. i18n: column header in table displaying installed keyboards
#: keyman_config/view_installed.py:141
msgid "Icon"
msgstr "Icon"
#. i18n: column header in table displaying installed keyboards
#: keyman_config/view_installed.py:145
msgid "Name"
msgstr "Name"
#. i18n: column header in table displaying installed keyboards
#: keyman_config/view_installed.py:148
msgid "Version"
msgstr "Version"
#: keyman_config/view_installed.py:161
msgid "_Uninstall"
msgstr "_Deinstallieren"
#: keyman_config/view_installed.py:162 keyman_config/view_installed.py:304
msgid "Uninstall keyboard"
msgstr "Tastatur deinstallieren"
#: keyman_config/view_installed.py:167
msgid "_About"
msgstr "_Über"
#: keyman_config/view_installed.py:168 keyman_config/view_installed.py:306
msgid "About keyboard"
msgstr "Über die Tastatur"
#: keyman_config/view_installed.py:173
msgid "_Help"
msgstr "_Hilfe"
#: keyman_config/view_installed.py:174 keyman_config/view_installed.py:305
msgid "Help for keyboard"
msgstr "Hilfe zur Tastatur"
#: keyman_config/view_installed.py:179
msgid "_Options"
msgstr "_Optionen"
#: keyman_config/view_installed.py:180 keyman_config/view_installed.py:307
msgid "Settings for keyboard"
msgstr "Einstellungen für Tastatur"
#: keyman_config/view_installed.py:190
msgid "_Refresh"
msgstr "_Aktualisieren"
#: keyman_config/view_installed.py:191
msgid "Refresh keyboard list"
msgstr "Tastaturliste aktualisieren"
#: keyman_config/view_installed.py:195
msgid "_Download"
msgstr "_Download"
#: keyman_config/view_installed.py:196
msgid "Download and install a keyboard from the Keyman website"
msgstr "Laden und installieren Sie eine Tastatur von der Keyman-Website"
#: keyman_config/view_installed.py:201
msgid "Install a keyboard from a file"
msgstr "Tastatur aus einer Datei installieren"
#: keyman_config/view_installed.py:206
msgid "Close window"
msgstr "Fenster schließen"
#: keyman_config/view_installed.py:278
#, python-brace-format
msgid "Uninstall keyboard {package}"
msgstr "Tastatur {package} deinstallieren"
#: keyman_config/view_installed.py:280
#, python-brace-format
msgid "Help for keyboard {package}"
msgstr "Hilfe zur Tastatur {package}"
#: keyman_config/view_installed.py:282
#, python-brace-format
msgid "About keyboard {package}"
msgstr "Über die Tastatur {package}"
#: keyman_config/view_installed.py:284
#, python-brace-format
msgid "Settings for keyboard {package}"
msgstr "Einstellungen für Tastatur {package}"
#: keyman_config/view_installed.py:349
msgid "Uninstall keyboard package?"
msgstr "Tastaturpaket deinstallieren?"
#: keyman_config/view_installed.py:351
#, python-brace-format
msgid "Are you sure that you want to uninstall the {keyboard} keyboard and its fonts?"
msgstr "Sind Sie sicher, dass Sie die {keyboard} -Tastatur und zugehörige Schriftarten deinstallieren möchten?"
#: keyman_config/welcome.py:22
#, python-brace-format
msgid "{name} installed"
msgstr "{name} installiert"
#: keyman_config/welcome.py:40
msgid "Open in _Web browser"
msgstr "Im _Web-Browser öffnen"
#: keyman_config/welcome.py:42
msgid "Open in the default web browser to do things like printing"
msgstr "Im Standard-Browser öffnen, z.B. um zu Drucken"
#: keyman_config/welcome.py:45
msgid "_OK"
msgstr "_OK"

View file

@ -29,6 +29,7 @@ for proj in ${projects}; do
downloadSource debianpackage
cd ${proj}-${version}
dch -v ${version}-1 "Re-release to Debian"
debuild -d -S -sa -Zxz
cd ${BASEDIR}
done

View file

@ -39,8 +39,8 @@ function downloadSource() {
make clean
fi
# Update tier in Debian watch files (replacing any previously set tier)
sed "s/\$tier\|alpha\|beta\|stable/${TIER}/g" $BASEDIR/scripts/watch.in > debian/watch
# Update tier in Debian watch files (replacing any previously set tier) and remove comment
sed -e "s/\$tier\|alpha\|beta\|stable/${TIER}/g" -e "s/^# .*$//" $BASEDIR/scripts/watch.in > debian/watch
version=$(uscan --report --dehs|xmllint --xpath "//dehs/upstream-version/text()" -)
dirversion=$(uscan --report --dehs|xmllint --xpath "//dehs/upstream-url/text()" - | cut -d/ -f6)

View file

@ -1,3 +1,3 @@
version=4
# Tier set by launchpad.sh script
# Tier replaced by package-build.inc.sh script
https://downloads.keyman.com/linux/$tier/@ANY_VERSION@/@PACKAGE@@ANY_VERSION@@ARCHIVE_EXT@ debian uupdate

View file

@ -11,11 +11,13 @@ import android.view.Display;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.widget.FrameLayout;
import com.tavultesoft.kmea.KMHardwareKeyboardInterpreter;
import com.tavultesoft.kmea.KMManager;
@ -162,6 +164,16 @@ public class SystemKeyboard extends InputMethodService implements KeyboardEventH
KMManager.onConfigurationChanged(newConfig);
}
@Override
public void onConfigureWindow(Window win, boolean isFullscreen, boolean isCandidatesOnly) {
super.onConfigureWindow(win, isFullscreen, isCandidatesOnly);
// We don't currently use isFullscreen or isCandidatesOnly; we always want to MATCH_PARENT,
// unlike the default for height which is WRAP_CONTENT. We then adjust the touchable area
// in `onCalculateInsets`
win.setLayout(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
}
@Override
public void onComputeInsets(Insets outInsets) {
super.onComputeInsets(outInsets);
@ -182,6 +194,7 @@ public class SystemKeyboard extends InputMethodService implements KeyboardEventH
int bannerHeight = KMManager.getBannerHeight(this);
int kbHeight = KMManager.getKeyboardHeight(this);
outInsets.contentTopInsets = inputViewHeight - bannerHeight - kbHeight;
outInsets.visibleTopInsets = outInsets.contentTopInsets;
outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_REGION;
outInsets.touchableRegion.set(0, outInsets.contentTopInsets, size.x, size.y);
}

View file

@ -1,6 +1,6 @@
# Localization Maintenance
Localization for Keyman is maintained at https://crowdin.com/project/keyman
Localization for Keyman is maintained at https://translate.keyman.com
Downloading and updating files between Keyman and Crowdin happens automatically
on GitHub by way of the Crowdin git integration. The configuration file for all platforms
@ -69,6 +69,9 @@ To download latest translations for the specific language:
crowdin download -b master -l {language_code}
```
Note: the Crowdin API doesn't handle custom languages so those will need to be manually synced.
See https://support.crowdin.com/api/language-codes/
To display a list of latest translations from Crowdin:
```bash
@ -83,6 +86,12 @@ To upload source files to Crowdin:
crowdin upload sources
```
### Updating DisplayLanguages.java in Keyman Engine for Android
In Keyman for Android, the settings menu for changing display languages is maintained in
android/KMEA/app/src/main/java/com/tavultesoft/kmea/DisplayLangugages.java
For the BCP-47 language tags to use in that file, don't include script names since the Android locales only handle language ID and region.
## Tip
Microsoft offers a website that allows to check how terms used in Windows are translated

View file

@ -1,5 +1,5 @@
{
"name": "@keymanapp/resources-gosh",
"version": "14.0.222",
"version": "14.0.227",
"lockfileVersion": 1
}

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/resources-gosh",
"version": "14.0.222",
"version": "14.0.227",
"description": "Good Ol' Shell",
"author": "Marc Durdin <marc@keyman.com> (https://github.com/mcdurdin)",
"contributors": [

View file

@ -251,13 +251,20 @@ set_version ( ) {
# set_npm_version VERSION_WITH_TIER
#
set_npm_version () {
if [ $# == 0 ]; then
fail "set_npm_version requires a specified version."
fi
local version=$1
# We use --no-git-tag-version because our CI system controls version numbering and
# already tags releases. We also want to have the version of this match the
# release of Keyman Developer -- these two versions should be in sync. Because this
# is a large repo with multiple projects and build systems, it's better for us that
# individual build systems don't take too much ownership of git tagging. :)
npm --no-git-tag-version --allow-same-version version "$version" || fail "Could not set package version to $version."
pushd . > /dev/null
cd "${KEYMAN_ROOT}"
npm run version "$version" || fail "Could not set package versions to $version."
popd > /dev/null
}
# Initializes use of the npm `lerna` package within the repo.

View file

@ -1,5 +1,5 @@
{
"name": "@keymanapp/web-environment",
"version": "14.0.222",
"version": "14.0.227",
"lockfileVersion": 1
}

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/web-environment",
"version": "14.0.222",
"version": "14.0.227",
"description": "A compilation utility for passing select environment variables into our TS builds",
"author": "Joshua Horton <joshua_horton@sil.org> (https://github.com/jahorton)",
"homepage": "https://github.com/keymanapp/keyman",
@ -17,6 +17,6 @@
"url": "https://github.com/keymanapp/keyman/issues"
},
"devDependencies": {
"@keymanapp/resources-gosh": "^14.0.222"
"@keymanapp/resources-gosh": "^14.0.227"
}
}

2
web/package-lock.json generated
View file

@ -1,6 +1,6 @@
{
"name": "keyman",
"version": "14.0.222",
"version": "14.0.227",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View file

@ -1,6 +1,6 @@
{
"name": "keyman",
"version": "14.0.222",
"version": "14.0.227",
"description": "Facilitates text input in any language.",
"repository": {
"type": "git",
@ -18,8 +18,8 @@
},
"homepage": "https://github.com/keymanapp/keyman#readme",
"devDependencies": {
"@keymanapp/resources-gosh": "^14.0.222",
"@keymanapp/web-sentry-manager": "^14.0.222",
"@keymanapp/resources-gosh": "^14.0.227",
"@keymanapp/web-sentry-manager": "^14.0.227",
"@sentry/cli": "^1.52.3",
"chai": "^4.2.0",
"google-closure-compiler-java": "^20200224.0.0",
@ -52,13 +52,13 @@
"sentry-cli": "sentry-cli"
},
"dependencies": {
"@keymanapp/input-processor": "^14.0.222",
"@keymanapp/keyboard-processor": "^14.0.222",
"@keymanapp/lexical-model-layer": "^14.0.222",
"@keymanapp/models-types": "^14.0.222",
"@keymanapp/recorder-core": "^14.0.222",
"@keymanapp/web-environment": "^14.0.222",
"@keymanapp/web-utils": "^14.0.222",
"@keymanapp/input-processor": "^14.0.227",
"@keymanapp/keyboard-processor": "^14.0.227",
"@keymanapp/lexical-model-layer": "^14.0.227",
"@keymanapp/models-types": "^14.0.227",
"@keymanapp/recorder-core": "^14.0.227",
"@keymanapp/web-environment": "^14.0.227",
"@keymanapp/web-utils": "^14.0.227",
"@types/node": "^11.9.4",
"es6-shim": "^0.35.5",
"eventemitter3": "^4.0.0",

View file

@ -40,7 +40,9 @@
kmw.init({
attachType:'auto',
resources:''
});
}).then(function() {
loadKeyboards();
});
</script>
<!-- Add keyboard management script for local selection of keyboards to use -->
@ -49,7 +51,7 @@
</head>
<!-- Sample page HTML -->
<body onload='loadKeyboards();'>
<body>
<h2>KeymanWeb Sample Page - Fully Compiled/Minified Source</h2>
<div>

View file

@ -32,7 +32,9 @@
kmw.init({
attachType:'auto',
resources:''
});
}).then(function() {
loadKeyboards();
});
</script>
<!--
Keyboard specifications, with the keyboard name and language code for each keyboard that is to be available.
@ -56,7 +58,7 @@
</head>
<!-- Sample page HTML -->
<body onload='loadKeyboards();'>
<body>
<h2>KeymanWeb Multilingual Page Example</h2>
<div>

View file

@ -39,7 +39,9 @@
var kmw=window.keyman;
kmw.init({
attachType:'auto'
});
}).then(function() {
loadKeyboards();
});
</script>
<!-- Add keyboard management script for local selection of keyboards to use -->
@ -48,7 +50,7 @@
</head>
<!-- Sample page HTML -->
<body onload='loadKeyboards();'>
<body>
<h2>KeymanWeb Sample Page - Unminified Source</h2>
<div>

View file

@ -425,7 +425,11 @@ namespace com.keyman.dom {
// Cancel touch if moved up and off keyboard, unless popup keys visible
} else {
let base = this.baseElement;
let top = (base.offsetParent as HTMLElement).offsetTop + base.offsetTop;
let parent = base.parentElement; // parentElement is _Box
let top = base.offsetTop;
if(parent) {
top += parent.offsetTop;
}
let height = base.offsetHeight;
let yMin = Math.max(5, top - 0.25 * height);
let yMax = (top + height) + 0.25 * height;
@ -454,4 +458,4 @@ namespace com.keyman.dom {
}
}
}
}
}

View file

@ -363,7 +363,7 @@ if(!window['keyman']['ui']['name']) {
var _li1 = util['createElement']('li');
_li1.id = 'KMW_ButtonUI_KbdList';
var _a1 = util['createElement']('a');
_a1.appendChild(document.createTextNode('English'));
_a1.appendChild(document.createTextNode('(System keyboard)'));
_a1.onclick = ui._SelectKeyboard;
_a1.href = '#';

View file

@ -126,7 +126,7 @@ if(!window['keyman']['ui']['name']) {
ui.innerDiv.appendChild(ui.KeyboardSelector); //this may need to be moved up....
// Check required interface alignment and default keyboard
var opt=util['getOption']('ui'),dfltKeyboard='English';
var opt=util['getOption']('ui'),dfltKeyboard='(System keyboard)';
if(opt && typeof(opt) == 'object')
{
if(typeof(opt['position']) == 'string' && opt['position'] == 'right')
@ -237,7 +237,7 @@ if(!window['keyman']['ui']['name']) {
*/
ui.updateMenu = function(kbd,lg)
{
var i=0,value='English';
var i=0;
// This can be called during startup before fully initialized - ignore if so
if(!ui.initialized) return;

View file

@ -696,7 +696,7 @@ if(!window['keyman']['ui']['name']) {
var _li=util['createElement']('li');
var _a=util['createElement']('a');
_a.innerHTML='English';
_a.innerHTML='(System keyboard)';
_a.href="#";
_a.onclick = function() { return ui.selectKbd(-1); };
_a.id='KMWSel_$';

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