diff options
Diffstat (limited to 'java/src')
27 files changed, 758 insertions, 515 deletions
diff --git a/java/src/com/android/inputmethod/compat/LooperCompatUtils.java b/java/src/com/android/inputmethod/compat/LooperCompatUtils.java new file mode 100644 index 000000000..d647dbbd3 --- /dev/null +++ b/java/src/com/android/inputmethod/compat/LooperCompatUtils.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.compat; + +import android.os.Looper; + +import java.lang.reflect.Method; + +/** + * Helper to call Looper#quitSafely, which was introduced in API + * level 18 (Build.VERSION_CODES.JELLY_BEAN_MR2). + * + * In unit tests, we create lots of instances of LatinIME, which means we need to clean up + * some Loopers lest we leak file descriptors. In normal use on a device though, this is never + * necessary (although it does not hurt). + */ +public final class LooperCompatUtils { + private static final Method METHOD_quitSafely = CompatUtils.getMethod( + Looper.class, "quitSafely"); + + public static void quitSafely(final Looper looper) { + if (null != METHOD_quitSafely) { + CompatUtils.invoke(looper, null /* default return value */, METHOD_quitSafely); + } else { + looper.quit(); + } + } +} diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java index 8badaf4b9..4a8fa51ee 100644 --- a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java +++ b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java @@ -45,10 +45,8 @@ public class MetadataDbHelper extends SQLiteOpenHelper { // This is the first released version of the database that implements CLIENTID. It is // used to identify the versions for upgrades. This should never change going forward. private static final int METADATA_DATABASE_VERSION_WITH_CLIENTID = 6; - // This is the current database version. It should be updated when the database schema - // gets updated. It is passed to the framework constructor of SQLiteOpenHelper, so - // that's what the framework uses to track our database version. - private static final int METADATA_DATABASE_VERSION = 6; + // The current database version. + private static final int CURRENT_METADATA_DATABASE_VERSION = 7; private final static long NOT_A_DOWNLOAD_ID = -1; @@ -169,7 +167,7 @@ public class MetadataDbHelper extends SQLiteOpenHelper { private MetadataDbHelper(final Context context, final String clientId) { super(context, METADATA_DATABASE_NAME_STEM + (TextUtils.isEmpty(clientId) ? "" : "." + clientId), - null, METADATA_DATABASE_VERSION); + null, CURRENT_METADATA_DATABASE_VERSION); mContext = context; mClientId = clientId; } @@ -219,22 +217,45 @@ public class MetadataDbHelper extends SQLiteOpenHelper { /** * Upgrade the database. Upgrade from version 3 is supported. + * Version 3 has a DB named METADATA_DATABASE_NAME_STEM containing a table METADATA_TABLE_NAME. + * Version 6 and above has a DB named METADATA_DATABASE_NAME_STEM containing a + * table CLIENT_TABLE_NAME, and for each client a table called METADATA_TABLE_STEM + "." + the + * name of the client and contains a table METADATA_TABLE_NAME. + * For schemas, see the above create statements. The schemas have never changed so far. + * + * This method is called by the framework. See {@link SQLiteOpenHelper#onUpgrade} + * @param db The database we are upgrading + * @param oldVersion The old database version (the one on the disk) + * @param newVersion The new database version as supplied to the constructor of SQLiteOpenHelper */ @Override public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) { if (METADATA_DATABASE_INITIAL_VERSION == oldVersion - && METADATA_DATABASE_VERSION_WITH_CLIENTID == newVersion) { + && METADATA_DATABASE_VERSION_WITH_CLIENTID <= newVersion + && CURRENT_METADATA_DATABASE_VERSION >= newVersion) { // Upgrade from version METADATA_DATABASE_INITIAL_VERSION to version // METADATA_DATABASE_VERSION_WITH_CLIENT_ID + // Only the default database should contain the client table, so we test for mClientId. if (TextUtils.isEmpty(mClientId)) { - // Only the default database should contain the client table. - // Anyway in version 3 only the default table existed so the emptyness + // Anyway in version 3 only the default table existed so the emptiness // test should always be true, but better check to be sure. createClientTable(db); } + } else if (METADATA_DATABASE_VERSION_WITH_CLIENTID < newVersion + && CURRENT_METADATA_DATABASE_VERSION >= newVersion) { + // Here we drop the client table, so that all clients send us their information again. + // The client table contains the URL to hit to update the available dictionaries list, + // but the info about the dictionaries themselves is stored in the table called + // METADATA_TABLE_NAME and we want to keep it, so we only drop the client table. + db.execSQL("DROP TABLE IF EXISTS " + CLIENT_TABLE_NAME); + // Only the default database should contain the client table, so we test for mClientId. + if (TextUtils.isEmpty(mClientId)) { + createClientTable(db); + } } else { - // Version 3 was the earliest version, so we should never come here. If we do, we - // have no idea what this database is, so we'd better wipe it off. + // If we're not in the above case, either we are upgrading from an earlier versionCode + // and we should wipe the database, or we are handling a version we never heard about + // (can only be a bug) so it's safer to wipe the database. db.execSQL("DROP TABLE IF EXISTS " + METADATA_TABLE_NAME); db.execSQL("DROP TABLE IF EXISTS " + CLIENT_TABLE_NAME); onCreate(db); diff --git a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java index 7e2166769..4edc61690 100644 --- a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java +++ b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java @@ -518,6 +518,14 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange } @Override + public boolean dispatchTouchEvent(final MotionEvent ev) { + // Add here to the stack trace to nail down the {@link IllegalArgumentException} exception + // in MotionEvent that sporadically happens. + // TODO: Remove this override method once the issue has been addressed. + return super.dispatchTouchEvent(ev); + } + + @Override public void onTabChanged(final String tabId) { final int categoryId = mEmojiCategory.getCategoryId(tabId); setCurrentCategoryId(categoryId, false /* force */); diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index 0f9c39a80..d8dd93a35 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -30,7 +30,6 @@ import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.preference.PreferenceManager; import android.util.AttributeSet; -import android.util.DisplayMetrics; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -428,21 +427,11 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } private void locatePreviewPlacerView() { - if (mDrawingPreviewPlacerView.getParent() != null) { - return; - } - final int width = getWidth(); - final int height = getHeight(); - if (width == 0 || height == 0) { - // In transient state. - return; - } getLocationInWindow(mOriginCoords); - final DisplayMetrics dm = getResources().getDisplayMetrics(); - if (CoordinateUtils.y(mOriginCoords) < dm.heightPixels / 4) { - // In transient state. - return; - } + mDrawingPreviewPlacerView.setKeyboardViewGeometry(mOriginCoords, getWidth(), getHeight()); + } + + private void installPreviewPlacerView() { final View rootView = getRootView(); if (rootView == null) { Log.w(TAG, "Cannot find root view"); @@ -452,10 +441,9 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack // Note: It'd be very weird if we get null by android.R.id.content. if (windowContentView == null) { Log.w(TAG, "Cannot find android.R.id.content view to add DrawingPreviewPlacerView"); - } else { - windowContentView.addView(mDrawingPreviewPlacerView); - mDrawingPreviewPlacerView.setKeyboardViewGeometry(mOriginCoords, width, height); + return; } + windowContentView.addView(mDrawingPreviewPlacerView); } /** @@ -576,6 +564,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); + installPreviewPlacerView(); // Notify the ResearchLogger (development only diagnostics) that the keyboard view has // been attached. This is needed to properly show the splash screen, which requires that // the window token of the KeyboardView be non-null. diff --git a/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java b/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java index cd7dd6f18..3a72aed0d 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java +++ b/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java @@ -29,25 +29,37 @@ import com.android.inputmethod.keyboard.PointerTracker; public abstract class AbstractDrawingPreview { private final View mDrawingView; private boolean mPreviewEnabled; + private boolean mHasValidGeometry; protected AbstractDrawingPreview(final View drawingView) { mDrawingView = drawingView; } - public final View getDrawingView() { + protected final View getDrawingView() { return mDrawingView; } - public final void setPreviewEnabled(final boolean enabled) { - mPreviewEnabled = enabled; + protected final boolean isPreviewEnabled() { + return mPreviewEnabled && mHasValidGeometry; } - public boolean isPreviewEnabled() { - return mPreviewEnabled; + public final void setPreviewEnabled(final boolean enabled) { + mPreviewEnabled = enabled; } - public void setKeyboardGeometry(final int[] originCoords, final int width, final int height) { - // Default implementation is empty. + /** + * Set {@link MainKeyboardView} geometry and position in the {@link SoftInputWindow}. + * The class that is overriding this method must call this super implementation. + * + * @param originCoords the top-left coordinates of the {@link MainKeyboardView} in + * {@link SoftInputWindow} coordinate-system. This is unused but has a point in an + * extended class, such as {@link GestureTrailsDrawingPreview}. + * @param width the width of {@link MainKeyboardView}. + * @param height the height of {@link MainKeyboardView}. + */ + public void setKeyboardViewGeometry(final int[] originCoords, final int width, + final int height) { + mHasValidGeometry = (width > 0 && height > 0); } public abstract void onDeallocateMemory(); diff --git a/java/src/com/android/inputmethod/keyboard/internal/CustomViewPager.java b/java/src/com/android/inputmethod/keyboard/internal/CustomViewPager.java deleted file mode 100644 index f5cc45ca7..000000000 --- a/java/src/com/android/inputmethod/keyboard/internal/CustomViewPager.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.keyboard.internal; - -import android.content.Context; -import android.support.v4.view.ViewPager; -import android.util.AttributeSet; -import android.util.Log; -import android.view.MotionEvent; - -/** - * Custom view pager to prevent {@link ViewPager} from crashing while handling multi-touch - * event. - */ -public class CustomViewPager extends ViewPager { - private static final String TAG = CustomViewPager.class.getSimpleName(); - - public CustomViewPager(final Context context, final AttributeSet attrs) { - super(context, attrs); - } - - @Override - public boolean onInterceptTouchEvent(final MotionEvent event) { - // This only happens when you multi-touch, take the first finger off and move. - // Unfortunately this causes {@link ViewPager} to crash, so we will ignore such events. - if (event.getAction() == MotionEvent.ACTION_MOVE && event.getPointerId(0) != 0) { - Log.w(TAG, "Ignored multi-touch move event to prevent ViewPager from crashing"); - return false; - } - - return super.onInterceptTouchEvent(event); - } -} diff --git a/java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java index 606addcc4..fdc2458d4 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java +++ b/java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java @@ -55,7 +55,7 @@ public final class DrawingPreviewPlacerView extends RelativeLayout { CoordinateUtils.copy(mKeyboardViewOrigin, originCoords); final int count = mPreviews.size(); for (int i = 0; i < count; i++) { - mPreviews.get(i).setKeyboardGeometry(originCoords, width, height); + mPreviews.get(i).setKeyboardViewGeometry(originCoords, width, height); } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsDrawingPreview.java b/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsDrawingPreview.java index eef4b36ed..d8b00c707 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsDrawingPreview.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsDrawingPreview.java @@ -95,7 +95,9 @@ public final class GestureTrailsDrawingPreview extends AbstractDrawingPreview { } @Override - public void setKeyboardGeometry(final int[] originCoords, final int width, final int height) { + public void setKeyboardViewGeometry(final int[] originCoords, final int width, + final int height) { + super.setKeyboardViewGeometry(originCoords, width, height); mOffscreenOffsetY = (int)(height * GestureStrokeRecognitionPoints.EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO); mOffscreenWidth = width; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java index 56acdde8d..0047aa4a1 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java @@ -41,8 +41,7 @@ public final class KeyboardTextsSet { private HashMap<String, String> mResourceNameToTextsMap = CollectionUtils.newHashMap(); public void setLocale(final Locale locale, final Context context) { - final String language = locale.getLanguage(); - mTextsTable = KeyboardTextsTable.getTextsTable(language); + mTextsTable = KeyboardTextsTable.getTextsTable(locale); final Resources res = context.getResources(); final int referenceId = context.getApplicationInfo().labelRes; final String resourcePackageName = res.getResourcePackageName(referenceId); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java index fc67f0879..37514be91 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java @@ -19,6 +19,7 @@ package com.android.inputmethod.keyboard.internal; import com.android.inputmethod.latin.utils.CollectionUtils; import java.util.HashMap; +import java.util.Locale; /** * !!!!! DO NOT EDIT THIS FILE !!!!! @@ -44,19 +45,19 @@ import java.util.HashMap; public final class KeyboardTextsTable { // Name to index map. private static final HashMap<String, Integer> sNameToIndexesMap = CollectionUtils.newHashMap(); - // Language to texts table map. - private static final HashMap<String, String[]> sLanguageToTextsTableMap = + // Locale to texts table map. + private static final HashMap<String, String[]> sLocaleToTextsTableMap = CollectionUtils.newHashMap(); // TODO: Remove this variable after debugging. - // Texts table to language maps. - private static final HashMap<String[], String> sTextsTableToLanguageMap = + // Texts table to locale maps. + private static final HashMap<String[], String> sTextsTableToLocaleMap = CollectionUtils.newHashMap(); public static String getText(final String name, final String[] textsTable) { final Integer indexObj = sNameToIndexesMap.get(name); if (indexObj == null) { - throw new RuntimeException("Unknown text name=" + name + " language=" - + sTextsTableToLanguageMap.get(textsTable)); + throw new RuntimeException("Unknown text name=" + name + " locale=" + + sTextsTableToLocaleMap.get(textsTable)); } final int index = indexObj; final String text = (index < textsTable.length) ? textsTable[index] : null; @@ -64,17 +65,24 @@ public final class KeyboardTextsTable { return text; } // Sanity check. - if (index >= 0 && index < LANGUAGE_DEFAULT.length) { - return LANGUAGE_DEFAULT[index]; + if (index >= 0 && index < TEXTS_DEFAULT.length) { + return TEXTS_DEFAULT[index]; } // Throw exception for debugging purpose. throw new RuntimeException("Illegal index=" + index + " for name=" + name - + " language=" + sTextsTableToLanguageMap.get(textsTable)); + + " locale=" + sTextsTableToLocaleMap.get(textsTable)); } - public static String[] getTextsTable(final String language) { - final String[] textsTable = sLanguageToTextsTableMap.get(language); - return textsTable != null ? textsTable : LANGUAGE_DEFAULT; + public static String[] getTextsTable(final Locale locale) { + final String localeKey = locale.toString(); + if (sLocaleToTextsTableMap.containsKey(localeKey)) { + return sLocaleToTextsTableMap.get(localeKey); + } + final String languageKey = locale.getLanguage(); + if (sLocaleToTextsTableMap.containsKey(languageKey)) { + return sLocaleToTextsTableMap.get(languageKey); + } + return TEXTS_DEFAULT; } private static final String[] NAMES = { @@ -133,43 +141,43 @@ public final class KeyboardTextsTable { /* 51: 4 */ "additional_morekeys_symbols_8", /* 52: 4 */ "additional_morekeys_symbols_9", /* 53: 4 */ "additional_morekeys_symbols_0", - /* 54: 3 */ "morekeys_punctuation", - /* 55: 3 */ "morekeys_star", - /* 56: 3 */ "keyspec_left_parenthesis", - /* 57: 3 */ "keyspec_right_parenthesis", - /* 58: 3 */ "keyspec_left_square_bracket", - /* 59: 3 */ "keyspec_right_square_bracket", - /* 60: 3 */ "keyspec_left_curly_bracket", - /* 61: 3 */ "keyspec_right_curly_bracket", - /* 62: 3 */ "keyspec_less_than", - /* 63: 3 */ "keyspec_greater_than", - /* 64: 3 */ "keyspec_less_than_equal", - /* 65: 3 */ "keyspec_greater_than_equal", - /* 66: 3 */ "keyspec_left_double_angle_quote", - /* 67: 3 */ "keyspec_right_double_angle_quote", - /* 68: 3 */ "keyspec_left_single_angle_quote", - /* 69: 3 */ "keyspec_right_single_angle_quote", - /* 70: 3 */ "keyspec_tablet_comma", - /* 71: 3 */ "morekeys_tablet_period", - /* 72: 3 */ "morekeys_question", - /* 73: 2 */ "morekeys_h", - /* 74: 2 */ "morekeys_w", - /* 75: 2 */ "morekeys_east_slavic_row2_2", - /* 76: 2 */ "morekeys_cyrillic_u", - /* 77: 2 */ "morekeys_cyrillic_en", - /* 78: 2 */ "morekeys_cyrillic_ghe", - /* 79: 2 */ "morekeys_cyrillic_o", - /* 80: 2 */ "morekeys_cyrillic_i", - /* 81: 2 */ "keyspec_south_slavic_row1_6", - /* 82: 2 */ "keyspec_south_slavic_row2_11", - /* 83: 2 */ "keyspec_south_slavic_row3_1", - /* 84: 2 */ "keyspec_south_slavic_row3_8", - /* 85: 2 */ "keyspec_swiss_row1_11", - /* 86: 2 */ "keyspec_swiss_row2_10", - /* 87: 2 */ "keyspec_swiss_row2_11", - /* 88: 2 */ "morekeys_swiss_row1_11", - /* 89: 2 */ "morekeys_swiss_row2_10", - /* 90: 2 */ "morekeys_swiss_row2_11", + /* 54: 3 */ "keyspec_swiss_row1_11", + /* 55: 3 */ "keyspec_swiss_row2_10", + /* 56: 3 */ "keyspec_swiss_row2_11", + /* 57: 3 */ "morekeys_swiss_row1_11", + /* 58: 3 */ "morekeys_swiss_row2_10", + /* 59: 3 */ "morekeys_swiss_row2_11", + /* 60: 3 */ "morekeys_punctuation", + /* 61: 3 */ "morekeys_star", + /* 62: 3 */ "keyspec_left_parenthesis", + /* 63: 3 */ "keyspec_right_parenthesis", + /* 64: 3 */ "keyspec_left_square_bracket", + /* 65: 3 */ "keyspec_right_square_bracket", + /* 66: 3 */ "keyspec_left_curly_bracket", + /* 67: 3 */ "keyspec_right_curly_bracket", + /* 68: 3 */ "keyspec_less_than", + /* 69: 3 */ "keyspec_greater_than", + /* 70: 3 */ "keyspec_less_than_equal", + /* 71: 3 */ "keyspec_greater_than_equal", + /* 72: 3 */ "keyspec_left_double_angle_quote", + /* 73: 3 */ "keyspec_right_double_angle_quote", + /* 74: 3 */ "keyspec_left_single_angle_quote", + /* 75: 3 */ "keyspec_right_single_angle_quote", + /* 76: 3 */ "keyspec_tablet_comma", + /* 77: 3 */ "morekeys_tablet_period", + /* 78: 3 */ "morekeys_question", + /* 79: 2 */ "morekeys_h", + /* 80: 2 */ "morekeys_w", + /* 81: 2 */ "morekeys_east_slavic_row2_2", + /* 82: 2 */ "morekeys_cyrillic_u", + /* 83: 2 */ "morekeys_cyrillic_en", + /* 84: 2 */ "morekeys_cyrillic_ghe", + /* 85: 2 */ "morekeys_cyrillic_o", + /* 86: 2 */ "morekeys_cyrillic_i", + /* 87: 2 */ "keyspec_south_slavic_row1_6", + /* 88: 2 */ "keyspec_south_slavic_row2_11", + /* 89: 2 */ "keyspec_south_slavic_row3_1", + /* 90: 2 */ "keyspec_south_slavic_row3_8", /* 91: 2 */ "keyspec_spanish_row2_10", /* 92: 2 */ "morekeys_bullet", /* 93: 2 */ "morekeys_left_parenthesis", @@ -188,23 +196,23 @@ public final class KeyboardTextsTable { /* 106: 2 */ "morekeys_symbols_percent", /* 107: 1 */ "morekeys_v", /* 108: 1 */ "morekeys_j", - /* 109: 1 */ "morekeys_east_slavic_row2_11", - /* 110: 1 */ "morekeys_cyrillic_ka", - /* 111: 1 */ "morekeys_cyrillic_a", - /* 112: 1 */ "morekeys_currency_dollar", - /* 113: 1 */ "morekeys_tablet_punctuation", - /* 114: 1 */ "morekeys_plus", - /* 115: 1 */ "morekeys_less_than", - /* 116: 1 */ "morekeys_greater_than", - /* 117: 1 */ "keyspec_period", - /* 118: 1 */ "keyspec_tablet_period", - /* 119: 1 */ "morekeys_exclamation", - /* 120: 1 */ "morekeys_q", - /* 121: 1 */ "morekeys_x", - /* 122: 1 */ "keyspec_q", - /* 123: 1 */ "keyspec_w", - /* 124: 1 */ "keyspec_y", - /* 125: 1 */ "keyspec_x", + /* 109: 1 */ "morekeys_q", + /* 110: 1 */ "morekeys_x", + /* 111: 1 */ "keyspec_q", + /* 112: 1 */ "keyspec_w", + /* 113: 1 */ "keyspec_y", + /* 114: 1 */ "keyspec_x", + /* 115: 1 */ "morekeys_east_slavic_row2_11", + /* 116: 1 */ "morekeys_cyrillic_ka", + /* 117: 1 */ "morekeys_cyrillic_a", + /* 118: 1 */ "morekeys_currency_dollar", + /* 119: 1 */ "morekeys_tablet_punctuation", + /* 120: 1 */ "morekeys_plus", + /* 121: 1 */ "morekeys_less_than", + /* 122: 1 */ "morekeys_greater_than", + /* 123: 1 */ "keyspec_period", + /* 124: 1 */ "keyspec_tablet_period", + /* 125: 1 */ "morekeys_exclamation", /* 126: 0 */ "morekeys_currency", /* 127: 0 */ "morekeys_symbols_1", /* 128: 0 */ "morekeys_symbols_2", @@ -252,7 +260,7 @@ public final class KeyboardTextsTable { private static final String EMPTY = ""; /* Default texts */ - private static final String[] LANGUAGE_DEFAULT = { + private static final String[] TEXTS_DEFAULT = { /* morekeys_a ~ */ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, /* ~ morekeys_c */ @@ -283,8 +291,9 @@ public final class KeyboardTextsTable { // Label for "switch to symbols" key. /* keylabel_to_symbol */ "?123", /* additional_morekeys_symbols_1 ~ */ - EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, - /* ~ additional_morekeys_symbols_0 */ + EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, + EMPTY, EMPTY, EMPTY, + /* ~ morekeys_swiss_row2_11 */ /* morekeys_punctuation */ "!autoColumnOrder!8,\\,,?,!,#,!text/keyspec_right_parenthesis,!text/keyspec_left_parenthesis,/,;,',@,:,-,\",+,\\%,&", // U+2020: "†" DAGGER // U+2021: "‡" DOUBLE DAGGER @@ -317,9 +326,8 @@ public final class KeyboardTextsTable { // U+00BF: "¿" INVERTED QUESTION MARK /* morekeys_question */ "\u00BF", /* morekeys_h ~ */ - EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, - EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, - /* ~ morekeys_swiss_row2_11 */ + EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, + /* ~ keyspec_south_slavic_row3_8 */ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE /* keyspec_spanish_row2_10 */ "\u00F1", // U+266A: "♪" EIGHTH NOTE @@ -345,7 +353,14 @@ public final class KeyboardTextsTable { // U+2030: "‰" PER MILLE SIGN /* morekeys_symbols_percent */ "\u2030", /* morekeys_v ~ */ - EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, + EMPTY, EMPTY, EMPTY, EMPTY, + /* ~ morekeys_x */ + /* keyspec_q */ "q", + /* keyspec_w */ "w", + /* keyspec_y */ "y", + /* keyspec_x */ "x", + /* morekeys_east_slavic_row2_11 ~ */ + EMPTY, EMPTY, EMPTY, /* ~ morekeys_cyrillic_a */ // U+00A2: "¢" CENT SIGN // U+00A3: "£" POUND SIGN @@ -363,12 +378,6 @@ public final class KeyboardTextsTable { /* keyspec_tablet_period */ ".", // U+00A1: "¡" INVERTED EXCLAMATION MARK /* morekeys_exclamation */ "\u00A1", - /* morekeys_q */ EMPTY, - /* morekeys_x */ EMPTY, - /* keyspec_q */ "q", - /* keyspec_w */ "w", - /* keyspec_y */ "y", - /* keyspec_x */ "x", /* morekeys_currency */ "$,\u00A2,\u20AC,\u00A3,\u00A5,\u20B1", // U+00B9: "¹" SUPERSCRIPT ONE // U+00BD: "½" VULGAR FRACTION ONE HALF @@ -456,8 +465,8 @@ public final class KeyboardTextsTable { /* keyspec_emoji_key */ "!icon/emoji_key|!code/key_emoji", }; - /* Language af: Afrikaans */ - private static final String[] LANGUAGE_af = { + /* Locale af: Afrikaans */ + private static final String[] TEXTS_af = { // This is the same as Dutch except more keys of y and demoting vowels with diaeresis. // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX @@ -512,8 +521,8 @@ public final class KeyboardTextsTable { /* morekeys_y */ "\u00FD,\u0133", }; - /* Language ar: Arabic */ - private static final String[] LANGUAGE_ar = { + /* Locale ar: Arabic */ + private static final String[] TEXTS_ar = { /* morekeys_a ~ */ null, null, null, null, null, null, null, null, null, /* ~ single_quotes */ @@ -562,7 +571,9 @@ public final class KeyboardTextsTable { // U+066B: "٫" ARABIC DECIMAL SEPARATOR // U+066C: "٬" ARABIC THOUSANDS SEPARATOR /* additional_morekeys_symbols_0 */ "0,\u066B,\u066C", - /* morekeys_punctuation */ null, + /* keyspec_swiss_row1_11 ~ */ + null, null, null, null, null, null, null, + /* ~ morekeys_punctuation */ // U+2605: "★" BLACK STAR // U+066D: "٭" ARABIC FIVE POINTED STAR /* morekeys_star */ "\u2605,\u066D", @@ -594,8 +605,7 @@ public final class KeyboardTextsTable { // U+00BF: "¿" INVERTED QUESTION MARK /* morekeys_question */ "?,\u00BF", /* morekeys_h ~ */ - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, /* ~ keyspec_spanish_row2_10 */ // U+266A: "♪" EIGHTH NOTE /* morekeys_bullet */ "\u266A", @@ -639,8 +649,8 @@ public final class KeyboardTextsTable { /* morekeys_symbols_percent */ "\\%,\u2030", }; - /* Language az_AZ: Azerbaijani (Azerbaijan) */ - private static final String[] LANGUAGE_az_AZ = { + /* Locale az_AZ: Azerbaijani (Azerbaijan) */ + private static final String[] TEXTS_az_AZ = { // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX /* morekeys_a */ "\u00E2", // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS @@ -687,8 +697,8 @@ public final class KeyboardTextsTable { /* morekeys_g */ "\u011F", }; - /* Language be_BY: Belarusian (Belarus) */ - private static final String[] LANGUAGE_be_BY = { + /* Locale be_BY: Belarusian (Belarus) */ + private static final String[] TEXTS_be_BY = { /* morekeys_a ~ */ null, null, null, null, null, null, /* ~ morekeys_c */ @@ -720,8 +730,8 @@ public final class KeyboardTextsTable { /* morekeys_cyrillic_soft_sign */ "\u044A", }; - /* Language bg: Bulgarian */ - private static final String[] LANGUAGE_bg = { + /* Locale bg: Bulgarian */ + private static final String[] TEXTS_bg = { /* morekeys_a ~ */ null, null, null, null, null, null, /* ~ morekeys_c */ @@ -736,8 +746,8 @@ public final class KeyboardTextsTable { /* keylabel_to_alpha */ "\u0410\u0411\u0412", }; - /* Language ca: Catalan */ - private static final String[] LANGUAGE_ca = { + /* Locale ca: Catalan */ + private static final String[] TEXTS_ca = { // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS @@ -797,26 +807,25 @@ public final class KeyboardTextsTable { /* morekeys_g ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, - /* ~ additional_morekeys_symbols_0 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~ morekeys_swiss_row2_11 */ // U+00B7: "·" MIDDLE DOT /* morekeys_punctuation */ "!autoColumnOrder!9,\\,,?,!,\u00B7,#,),(,/,;,',@,:,-,\",+,\\%,&", /* morekeys_star ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, - /* ~ morekeys_swiss_row2_11 */ + /* ~ keyspec_south_slavic_row3_8 */ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA /* keyspec_spanish_row2_10 */ "\u00E7", /* morekeys_bullet ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_currency_dollar */ /* morekeys_tablet_punctuation */ "!autoColumnOrder!8,\\,,',\u00B7,#,),(,/,;,@,:,-,\",+,\\%,&", }; - /* Language cs: Czech */ - private static final String[] LANGUAGE_cs = { + /* Locale cs: Czech */ + private static final String[] TEXTS_cs = { // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX @@ -893,8 +902,8 @@ public final class KeyboardTextsTable { /* morekeys_r */ "\u0159", }; - /* Language da: Danish */ - private static final String[] LANGUAGE_da = { + /* Locale da: Danish */ + private static final String[] TEXTS_da = { // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE @@ -962,8 +971,8 @@ public final class KeyboardTextsTable { /* morekeys_nordic_row2_11 */ "\u00F6", }; - /* Language de: German */ - private static final String[] LANGUAGE_de = { + /* Locale de: German */ + private static final String[] TEXTS_de = { // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE @@ -972,7 +981,7 @@ public final class KeyboardTextsTable { // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON - /* morekeys_a */ "\u00E4,\u00E2,\u00E0,\u00E1,\u00E6,\u00E3,\u00E5,\u0101", + /* morekeys_a */ "\u00E4,%,\u00E2,\u00E0,\u00E1,\u00E6,\u00E3,\u00E5,\u0101", // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE @@ -981,13 +990,13 @@ public final class KeyboardTextsTable { // U+0153: "œ" LATIN SMALL LIGATURE OE // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON - /* morekeys_o */ "\u00F6,\u00F4,\u00F2,\u00F3,\u00F5,\u0153,\u00F8,\u014D", + /* morekeys_o */ "\u00F6,%,\u00F4,\u00F2,\u00F3,\u00F5,\u0153,\u00F8,\u014D", // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON - /* morekeys_u */ "\u00FC,\u00FB,\u00F9,\u00FA,\u016B", + /* morekeys_u */ "\u00FC,%,\u00FB,\u00F9,\u00FA,\u016B", // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX @@ -1014,10 +1023,8 @@ public final class KeyboardTextsTable { /* keyspec_currency ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, - /* ~ keyspec_south_slavic_row3_8 */ + null, null, null, null, null, + /* ~ additional_morekeys_symbols_0 */ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS /* keyspec_swiss_row1_11 */ "\u00FC", // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS @@ -1032,8 +1039,8 @@ public final class KeyboardTextsTable { /* morekeys_swiss_row2_11 */ "\u00E0", }; - /* Language el: Greek */ - private static final String[] LANGUAGE_el = { + /* Locale el: Greek */ + private static final String[] TEXTS_el = { /* morekeys_a ~ */ null, null, null, null, null, null, null, null, null, /* ~ single_quotes */ @@ -1044,8 +1051,8 @@ public final class KeyboardTextsTable { /* keylabel_to_alpha */ "\u0391\u0392\u0393", }; - /* Language en: English */ - private static final String[] LANGUAGE_en = { + /* Locale en: English */ + private static final String[] TEXTS_en = { // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX @@ -1093,8 +1100,8 @@ public final class KeyboardTextsTable { /* morekeys_s */ "\u00DF", }; - /* Language eo: Esperanto */ - private static final String[] LANGUAGE_eo = { + /* Locale eo: Esperanto */ + private static final String[] TEXTS_eo = { // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX @@ -1211,7 +1218,7 @@ public final class KeyboardTextsTable { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_question */ // U+0125: "ĥ" LATIN SMALL LETTER H WITH CIRCUMFLEX // U+0127: "ħ" LATIN SMALL LETTER H WITH STROKE @@ -1219,9 +1226,8 @@ public final class KeyboardTextsTable { // U+0175: "ŵ" LATIN SMALL LETTER W WITH CIRCUMFLEX /* morekeys_w */ "w,\u0175", /* morekeys_east_slavic_row2_2 ~ */ - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, - /* ~ morekeys_swiss_row2_11 */ + null, null, null, null, null, null, null, null, null, null, + /* ~ keyspec_south_slavic_row3_8 */ // U+0135: "ĵ" LATIN SMALL LETTER J WITH CIRCUMFLEX /* keyspec_spanish_row2_10 */ "\u0135", /* morekeys_bullet ~ */ @@ -1229,9 +1235,7 @@ public final class KeyboardTextsTable { /* ~ morekeys_symbols_percent */ // U+0175: "ŵ" LATIN SMALL LETTER W WITH CIRCUMFLEX /* morekeys_v */ "w,\u0175", - /* morekeys_j ~ */ - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~ morekeys_exclamation */ + /* morekeys_j */ null, /* morekeys_q */ "q", /* morekeys_x */ "x", // U+015D: "ŝ" LATIN SMALL LETTER S WITH CIRCUMFLEX @@ -1244,8 +1248,8 @@ public final class KeyboardTextsTable { /* keyspec_x */ "\u0109", }; - /* Language es: Spanish */ - private static final String[] LANGUAGE_es = { + /* Locale es: Spanish */ + private static final String[] TEXTS_es = { // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS @@ -1300,15 +1304,15 @@ public final class KeyboardTextsTable { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, - /* ~ additional_morekeys_symbols_0 */ + null, null, null, null, null, null, null, + /* ~ morekeys_swiss_row2_11 */ // U+00A1: "¡" INVERTED EXCLAMATION MARK // U+00BF: "¿" INVERTED QUESTION MARK /* morekeys_punctuation */ "!autoColumnOrder!9,\\,,?,!,#,),(,/,;,\u00A1,',@,:,-,\",+,\\%,&,\u00BF", }; - /* Language et_EE: Estonian (Estonia) */ - private static final String[] LANGUAGE_et_EE = { + /* Locale et_EE: Estonian (Estonia) */ + private static final String[] TEXTS_et_EE = { // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE @@ -1410,8 +1414,8 @@ public final class KeyboardTextsTable { /* morekeys_nordic_row2_10 */ "\u00F5", }; - /* Language eu_ES: Basque (Spain) */ - private static final String[] LANGUAGE_eu_ES = { + /* Locale eu_ES: Basque (Spain) */ + private static final String[] TEXTS_eu_ES = { // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS @@ -1464,8 +1468,8 @@ public final class KeyboardTextsTable { /* morekeys_n */ "\u00F1,\u0144", }; - /* Language fa: Persian */ - private static final String[] LANGUAGE_fa = { + /* Locale fa: Persian */ + private static final String[] TEXTS_fa = { /* morekeys_a ~ */ null, null, null, null, null, null, null, null, null, /* ~ single_quotes */ @@ -1518,7 +1522,9 @@ public final class KeyboardTextsTable { // U+066B: "٫" ARABIC DECIMAL SEPARATOR // U+066C: "٬" ARABIC THOUSANDS SEPARATOR /* additional_morekeys_symbols_0 */ "0,\u066B,\u066C", - /* morekeys_punctuation */ null, + /* keyspec_swiss_row1_11 ~ */ + null, null, null, null, null, null, null, + /* ~ morekeys_punctuation */ // U+2605: "★" BLACK STAR // U+066D: "٭" ARABIC FIVE POINTED STAR /* morekeys_star */ "\u2605,\u066D", @@ -1546,8 +1552,7 @@ public final class KeyboardTextsTable { // U+00BF: "¿" INVERTED QUESTION MARK /* morekeys_question */ "?,\u00BF", /* morekeys_h ~ */ - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, /* ~ keyspec_spanish_row2_10 */ // U+266A: "♪" EIGHTH NOTE /* morekeys_bullet */ "\u266A", @@ -1590,7 +1595,7 @@ public final class KeyboardTextsTable { // U+2030: "‰" PER MILLE SIGN /* morekeys_symbols_percent */ "\\%,\u2030", /* morekeys_v ~ */ - null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_plus */ // U+2264: "≤" LESS-THAN OR EQUAL TO // U+2265: "≥" GREATER-THAN EQUAL TO @@ -1602,8 +1607,8 @@ public final class KeyboardTextsTable { /* morekeys_greater_than */ "!fixedColumnOrder!3,!text/keyspec_right_single_angle_quote,!text/keyspec_greater_than_equal,!text/keyspec_greater_than", }; - /* Language fi: Finnish */ - private static final String[] LANGUAGE_fi = { + /* Locale fi: Finnish */ + private static final String[] TEXTS_fi = { // U+00E6: "æ" LATIN SMALL LETTER AE // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE @@ -1652,8 +1657,8 @@ public final class KeyboardTextsTable { /* morekeys_nordic_row2_11 */ "\u00E6", }; - /* Language fr: French */ - private static final String[] LANGUAGE_fr = { + /* Locale fr: French */ + private static final String[] TEXTS_fr = { // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX // U+00E6: "æ" LATIN SMALL LETTER AE @@ -1707,10 +1712,8 @@ public final class KeyboardTextsTable { /* morekeys_d ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~ keyspec_south_slavic_row3_8 */ + null, null, null, null, null, null, null, null, null, null, null, null, + /* ~ additional_morekeys_symbols_0 */ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE /* keyspec_swiss_row1_11 */ "\u00E8", // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE @@ -1725,8 +1728,8 @@ public final class KeyboardTextsTable { /* morekeys_swiss_row2_11 */ "\u00E4", }; - /* Language gl_ES: Gallegan (Spain) */ - private static final String[] LANGUAGE_gl_ES = { + /* Locale gl_ES: Gallegan (Spain) */ + private static final String[] TEXTS_gl_ES = { // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS @@ -1779,8 +1782,8 @@ public final class KeyboardTextsTable { /* morekeys_n */ "\u00F1,\u0144", }; - /* Language hi: Hindi */ - private static final String[] LANGUAGE_hi = { + /* Locale hi: Hindi */ + private static final String[] TEXTS_hi = { /* morekeys_a ~ */ null, null, null, null, null, null, null, null, null, /* ~ single_quotes */ @@ -1831,8 +1834,8 @@ public final class KeyboardTextsTable { /* additional_morekeys_symbols_0 */ "0", }; - /* Language hr: Croatian */ - private static final String[] LANGUAGE_hr = { + /* Locale hr: Croatian */ + private static final String[] TEXTS_hr = { /* morekeys_a ~ */ null, null, null, null, null, /* ~ morekeys_i */ @@ -1864,8 +1867,8 @@ public final class KeyboardTextsTable { /* double_angle_quotes */ "!text/double_raqm_laqm", }; - /* Language hu: Hungarian */ - private static final String[] LANGUAGE_hu = { + /* Locale hu: Hungarian */ + private static final String[] TEXTS_hu = { // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX @@ -1918,8 +1921,8 @@ public final class KeyboardTextsTable { /* double_angle_quotes */ "!text/double_raqm_laqm", }; - /* Language hy_AM: Armenian (Armenia) */ - private static final String[] LANGUAGE_hy_AM = { + /* Locale hy_AM: Armenian (Armenia) */ + private static final String[] TEXTS_hy_AM = { /* morekeys_a ~ */ null, null, null, null, null, null, null, null, null, /* ~ single_quotes */ @@ -1931,8 +1934,9 @@ public final class KeyboardTextsTable { /* morekeys_s ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~ additional_morekeys_symbols_0 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, + /* ~ morekeys_swiss_row2_11 */ // U+055E: "՞" ARMENIAN QUESTION MARK // U+055C: "՜" ARMENIAN EXCLAMATION MARK // U+055A: "՚" ARMENIAN APOSTROPHE @@ -1970,8 +1974,8 @@ public final class KeyboardTextsTable { /* morekeys_exclamation */ "\u055C,\u00A1", }; - /* Language is: Icelandic */ - private static final String[] LANGUAGE_is = { + /* Locale is: Icelandic */ + private static final String[] TEXTS_is = { // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS // U+00E6: "æ" LATIN SMALL LETTER AE @@ -2027,8 +2031,8 @@ public final class KeyboardTextsTable { /* morekeys_t */ "\u00FE", }; - /* Language it: Italian */ - private static final String[] LANGUAGE_it = { + /* Locale it: Italian */ + private static final String[] TEXTS_it = { // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX @@ -2070,10 +2074,28 @@ public final class KeyboardTextsTable { // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON /* morekeys_i */ "\u00EC,\u00ED,\u00EE,\u00EF,\u012F,\u012B", + /* morekeys_c ~ */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, + /* ~ additional_morekeys_symbols_0 */ + // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS + /* keyspec_swiss_row1_11 */ "\u00FC", + // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS + /* keyspec_swiss_row2_10 */ "\u00F6", + // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS + /* keyspec_swiss_row2_11 */ "\u00E4", + // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE + /* morekeys_swiss_row1_11 */ "\u00E8", + // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE + /* morekeys_swiss_row2_10 */ "\u00E9", + // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE + /* morekeys_swiss_row2_11 */ "\u00E0", }; - /* Language iw: Hebrew */ - private static final String[] LANGUAGE_iw = { + /* Locale iw: Hebrew */ + private static final String[] TEXTS_iw = { /* morekeys_a ~ */ null, null, null, null, null, null, /* ~ morekeys_c */ @@ -2093,7 +2115,7 @@ public final class KeyboardTextsTable { /* morekeys_r ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_punctuation */ // U+2605: "★" BLACK STAR /* morekeys_star */ "\u2605", @@ -2129,8 +2151,8 @@ public final class KeyboardTextsTable { /* morekeys_plus */ "\u00B1,\uFB29", }; - /* Language ka_GE: Georgian (Georgia) */ - private static final String[] LANGUAGE_ka_GE = { + /* Locale ka_GE: Georgian (Georgia) */ + private static final String[] TEXTS_ka_GE = { /* morekeys_a ~ */ null, null, null, null, null, null, /* ~ morekeys_c */ @@ -2144,8 +2166,8 @@ public final class KeyboardTextsTable { /* keylabel_to_alpha */ "\u10D0\u10D1\u10D2", }; - /* Language kk: Kazakh */ - private static final String[] LANGUAGE_kk = { + /* Locale kk: Kazakh */ + private static final String[] TEXTS_kk = { /* morekeys_a ~ */ null, null, null, null, null, null, null, null, null, /* ~ single_quotes */ @@ -2175,7 +2197,8 @@ public final class KeyboardTextsTable { /* morekeys_nordic_row2_11 ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, /* ~ morekeys_w */ // U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I /* morekeys_east_slavic_row2_2 */ "\u0456", @@ -2191,7 +2214,7 @@ public final class KeyboardTextsTable { /* morekeys_cyrillic_i ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~ morekeys_j */ + /* ~ keyspec_x */ // U+04BB: "һ" CYRILLIC SMALL LETTER SHHA /* morekeys_east_slavic_row2_11 */ "\u04BB", // U+049B: "қ" CYRILLIC SMALL LETTER KA WITH DESCENDER @@ -2200,8 +2223,8 @@ public final class KeyboardTextsTable { /* morekeys_cyrillic_a */ "\u04D9", }; - /* Language km_KH: Khmer (Cambodia) */ - private static final String[] LANGUAGE_km_KH = { + /* Locale km_KH: Khmer (Cambodia) */ + private static final String[] TEXTS_km_KH = { /* morekeys_a ~ */ null, null, null, null, null, null, null, null, null, /* ~ single_quotes */ @@ -2217,14 +2240,15 @@ public final class KeyboardTextsTable { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, /* ~ morekeys_cyrillic_a */ // U+17DB: "៛" KHMER CURRENCY SYMBOL RIEL /* morekeys_currency_dollar */ "\u17DB,\u00A2,\u00A3,\u20AC,\u00A5,\u20B1", }; - /* Language ky: Kirghiz */ - private static final String[] LANGUAGE_ky = { + /* Locale ky: Kirghiz */ + private static final String[] TEXTS_ky = { /* morekeys_a ~ */ null, null, null, null, null, null, null, null, null, /* ~ single_quotes */ @@ -2254,7 +2278,8 @@ public final class KeyboardTextsTable { /* morekeys_nordic_row2_11 ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, /* ~ morekeys_east_slavic_row2_2 */ // U+04AF: "ү" CYRILLIC SMALL LETTER STRAIGHT U /* morekeys_cyrillic_u */ "\u04AF", @@ -2265,8 +2290,8 @@ public final class KeyboardTextsTable { /* morekeys_cyrillic_o */ "\u04E9", }; - /* Language lo_LA: Lao (Laos) */ - private static final String[] LANGUAGE_lo_LA = { + /* Locale lo_LA: Lao (Laos) */ + private static final String[] TEXTS_lo_LA = { /* morekeys_a ~ */ null, null, null, null, null, null, null, null, null, /* ~ single_quotes */ @@ -2282,8 +2307,8 @@ public final class KeyboardTextsTable { /* keyspec_currency */ "\u20AD", }; - /* Language lt: Lithuanian */ - private static final String[] LANGUAGE_lt = { + /* Locale lt: Lithuanian */ + private static final String[] TEXTS_lt = { // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON @@ -2377,8 +2402,8 @@ public final class KeyboardTextsTable { /* morekeys_k */ "\u0137", }; - /* Language lv: Latvian */ - private static final String[] LANGUAGE_lv = { + /* Locale lv: Latvian */ + private static final String[] TEXTS_lv = { // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE @@ -2471,8 +2496,8 @@ public final class KeyboardTextsTable { /* morekeys_k */ "\u0137", }; - /* Language mk: Macedonian */ - private static final String[] LANGUAGE_mk = { + /* Locale mk: Macedonian */ + private static final String[] TEXTS_mk = { /* morekeys_a ~ */ null, null, null, null, null, null, /* ~ morekeys_c */ @@ -2493,7 +2518,8 @@ public final class KeyboardTextsTable { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, /* ~ morekeys_cyrillic_o */ // U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE /* morekeys_cyrillic_i */ "\u045D", @@ -2507,8 +2533,8 @@ public final class KeyboardTextsTable { /* keyspec_south_slavic_row3_8 */ "\u0453", }; - /* Language mn_MN: Mongolian (Mongolia) */ - private static final String[] LANGUAGE_mn_MN = { + /* Locale mn_MN: Mongolian (Mongolia) */ + private static final String[] TEXTS_mn_MN = { /* morekeys_a ~ */ null, null, null, null, null, null, null, null, null, /* ~ single_quotes */ @@ -2524,8 +2550,8 @@ public final class KeyboardTextsTable { /* keyspec_currency */ "\u20AE", }; - /* Language my_MM: Burmese (Myanmar) */ - private static final String[] LANGUAGE_my_MM = { + /* Locale my_MM: Burmese (Myanmar) */ + private static final String[] TEXTS_my_MM = { /* morekeys_a ~ */ null, null, null, null, null, null, null, null, null, /* ~ single_quotes */ @@ -2536,8 +2562,8 @@ public final class KeyboardTextsTable { /* keylabel_to_alpha */ "\u1000\u1001\u1002", }; - /* Language nb: Norwegian Bokmål */ - private static final String[] LANGUAGE_nb = { + /* Locale nb: Norwegian Bokmål */ + private static final String[] TEXTS_nb = { // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE @@ -2590,8 +2616,8 @@ public final class KeyboardTextsTable { /* morekeys_nordic_row2_11 */ "\u00E4", }; - /* Language ne_NP: Nepali (Nepal) */ - private static final String[] LANGUAGE_ne_NP = { + /* Locale ne_NP: Nepali (Nepal) */ + private static final String[] TEXTS_ne_NP = { /* morekeys_a ~ */ null, null, null, null, null, null, null, null, null, /* ~ single_quotes */ @@ -2642,8 +2668,8 @@ public final class KeyboardTextsTable { /* additional_morekeys_symbols_0 */ "0", }; - /* Language nl: Dutch */ - private static final String[] LANGUAGE_nl = { + /* Locale nl: Dutch */ + private static final String[] TEXTS_nl = { // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX @@ -2696,8 +2722,8 @@ public final class KeyboardTextsTable { /* morekeys_y */ "\u0133", }; - /* Language pl: Polish */ - private static final String[] LANGUAGE_pl = { + /* Locale pl: Polish */ + private static final String[] TEXTS_pl = { // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE @@ -2752,8 +2778,8 @@ public final class KeyboardTextsTable { /* morekeys_l */ "\u0142", }; - /* Language pt: Portuguese */ - private static final String[] LANGUAGE_pt = { + /* Locale pt: Portuguese */ + private static final String[] TEXTS_pt = { // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE @@ -2800,8 +2826,8 @@ public final class KeyboardTextsTable { /* morekeys_c */ "\u00E7,\u010D,\u0107", }; - /* Language rm: Raeto-Romance */ - private static final String[] LANGUAGE_rm = { + /* Locale rm: Raeto-Romance */ + private static final String[] TEXTS_rm = { /* morekeys_a */ null, // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE @@ -2813,8 +2839,8 @@ public final class KeyboardTextsTable { /* morekeys_o */ "\u00F2,\u00F3,\u00F6,\u00F4,\u00F5,\u0153,\u00F8", }; - /* Language ro: Romanian */ - private static final String[] LANGUAGE_ro = { + /* Locale ro: Romanian */ + private static final String[] TEXTS_ro = { // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE // U+0103: "ă" LATIN SMALL LETTER A WITH BREVE @@ -2852,8 +2878,8 @@ public final class KeyboardTextsTable { /* morekeys_t */ "\u021B", }; - /* Language ru: Russian */ - private static final String[] LANGUAGE_ru = { + /* Locale ru: Russian */ + private static final String[] TEXTS_ru = { /* morekeys_a ~ */ null, null, null, null, null, null, /* ~ morekeys_c */ @@ -2885,8 +2911,8 @@ public final class KeyboardTextsTable { /* morekeys_cyrillic_soft_sign */ "\u044A", }; - /* Language sk: Slovak */ - private static final String[] LANGUAGE_sk = { + /* Locale sk: Slovak */ + private static final String[] TEXTS_sk = { // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON @@ -2980,8 +3006,8 @@ public final class KeyboardTextsTable { /* morekeys_k */ "\u0137", }; - /* Language sl: Slovenian */ - private static final String[] LANGUAGE_sl = { + /* Locale sl: Slovenian */ + private static final String[] TEXTS_sl = { /* morekeys_a ~ */ null, null, null, null, null, /* ~ morekeys_i */ @@ -3006,8 +3032,8 @@ public final class KeyboardTextsTable { /* double_angle_quotes */ "!text/double_raqm_laqm", }; - /* Language sr: Serbian */ - private static final String[] LANGUAGE_sr = { + /* Locale sr: Serbian */ + private static final String[] TEXTS_sr = { /* morekeys_a ~ */ null, null, null, null, null, null, /* ~ morekeys_c */ @@ -3034,7 +3060,8 @@ public final class KeyboardTextsTable { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, /* ~ morekeys_cyrillic_o */ // U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE /* morekeys_cyrillic_i */ "\u045D", @@ -3066,8 +3093,8 @@ public final class KeyboardTextsTable { /* keyspec_south_slavic_row3_8 */ "\u0452", }; - /* Language sv: Swedish */ - private static final String[] LANGUAGE_sv = { + /* Locale sv: Swedish */ + private static final String[] TEXTS_sv = { // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX @@ -3152,8 +3179,8 @@ public final class KeyboardTextsTable { /* morekeys_nordic_row2_11 */ "\u00E6", }; - /* Language sw: Swahili */ - private static final String[] LANGUAGE_sw = { + /* Locale sw: Swahili */ + private static final String[] TEXTS_sw = { // This is the same as English except morekeys_g. // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE @@ -3206,8 +3233,8 @@ public final class KeyboardTextsTable { /* morekeys_g */ "g\'", }; - /* Language th: Thai */ - private static final String[] LANGUAGE_th = { + /* Locale th: Thai */ + private static final String[] TEXTS_th = { /* morekeys_a ~ */ null, null, null, null, null, null, null, null, null, /* ~ single_quotes */ @@ -3223,8 +3250,8 @@ public final class KeyboardTextsTable { /* keyspec_currency */ "\u0E3F", }; - /* Language tl: Tagalog */ - private static final String[] LANGUAGE_tl = { + /* Locale tl: Tagalog */ + private static final String[] TEXTS_tl = { // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS @@ -3277,8 +3304,8 @@ public final class KeyboardTextsTable { /* morekeys_n */ "\u00F1,\u0144", }; - /* Language tr: Turkish */ - private static final String[] LANGUAGE_tr = { + /* Locale tr: Turkish */ + private static final String[] TEXTS_tr = { // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX /* morekeys_a */ "\u00E2", // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS @@ -3324,8 +3351,8 @@ public final class KeyboardTextsTable { /* morekeys_g */ "\u011F", }; - /* Language uk: Ukrainian */ - private static final String[] LANGUAGE_uk = { + /* Locale uk: Ukrainian */ + private static final String[] TEXTS_uk = { /* morekeys_a ~ */ null, null, null, null, null, null, /* ~ morekeys_c */ @@ -3358,7 +3385,8 @@ public final class KeyboardTextsTable { /* morekeys_nordic_row2_11 ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, /* ~ morekeys_w */ // U+0457: "ї" CYRILLIC SMALL LETTER YI /* morekeys_east_slavic_row2_2 */ "\u0457", @@ -3368,8 +3396,8 @@ public final class KeyboardTextsTable { /* morekeys_cyrillic_ghe */ "\u0491", }; - /* Language vi: Vietnamese */ - private static final String[] LANGUAGE_vi = { + /* Locale vi: Vietnamese */ + private static final String[] TEXTS_vi = { // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE // U+1EA3: "ả" LATIN SMALL LETTER A WITH HOOK ABOVE @@ -3454,8 +3482,8 @@ public final class KeyboardTextsTable { /* keyspec_currency */ "\u20AB", }; - /* Language zu: Zulu */ - private static final String[] LANGUAGE_zu = { + /* Locale zu: Zulu */ + private static final String[] TEXTS_zu = { // This is the same as English // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE @@ -3504,8 +3532,8 @@ public final class KeyboardTextsTable { /* morekeys_s */ "\u00DF", }; - /* Language zz: Alphabet */ - private static final String[] LANGUAGE_zz = { + /* Locale zz: Alphabet */ + private static final String[] TEXTS_zz = { // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX @@ -3628,7 +3656,7 @@ public final class KeyboardTextsTable { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_question */ // U+0125: "ĥ" LATIN SMALL LETTER H WITH CIRCUMFLEX /* morekeys_h */ "\u0125", @@ -3636,74 +3664,71 @@ public final class KeyboardTextsTable { /* morekeys_w */ "\u0175", /* morekeys_east_slavic_row2_2 ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_v */ // U+0135: "ĵ" LATIN SMALL LETTER J WITH CIRCUMFLEX /* morekeys_j */ "\u0135", }; - // TODO: Use the language + "_" + region representation for the locale string key. - // Currently we are dropping the region from the key. - private static final Object[] LANGUAGES_AND_TEXTS = { + private static final Object[] LOCALES_AND_TEXTS = { // "locale", TEXT_ARRAY, /* numberOfNonNullText/lengthOf_TEXT_ARRAY localeName */ - "DEFAULT", LANGUAGE_DEFAULT, /* 168/168 default */ - "af", LANGUAGE_af, /* 7/ 12 Afrikaans */ - "ar", LANGUAGE_ar, /* 55/107 Arabic */ - "az", LANGUAGE_az_AZ, /* 8/ 17 Azerbaijani (Azerbaijan) */ - "be", LANGUAGE_be_BY, /* 9/ 32 Belarusian (Belarus) */ - "bg", LANGUAGE_bg, /* 2/ 10 Bulgarian */ - "ca", LANGUAGE_ca, /* 11/114 Catalan */ - "cs", LANGUAGE_cs, /* 17/ 21 Czech */ - "da", LANGUAGE_da, /* 19/ 33 Danish */ - "de", LANGUAGE_de, /* 16/ 91 German */ - "el", LANGUAGE_el, /* 1/ 10 Greek */ - "en", LANGUAGE_en, /* 8/ 11 English */ - "eo", LANGUAGE_eo, /* 26/126 Esperanto */ - "es", LANGUAGE_es, /* 8/ 55 Spanish */ - "et", LANGUAGE_et_EE, /* 22/ 27 Estonian (Estonia) */ - "eu", LANGUAGE_eu_ES, /* 7/ 8 Basque (Spain) */ - "fa", LANGUAGE_fa, /* 58/117 Persian */ - "fi", LANGUAGE_fi, /* 10/ 33 Finnish */ - "fr", LANGUAGE_fr, /* 13/ 91 French */ - "gl", LANGUAGE_gl_ES, /* 7/ 8 Gallegan (Spain) */ - "hi", LANGUAGE_hi, /* 23/ 54 Hindi */ - "hr", LANGUAGE_hr, /* 9/ 19 Croatian */ - "hu", LANGUAGE_hu, /* 9/ 19 Hungarian */ - "hy", LANGUAGE_hy_AM, /* 8/120 Armenian (Armenia) */ - "is", LANGUAGE_is, /* 10/ 15 Icelandic */ - "it", LANGUAGE_it, /* 5/ 5 Italian */ - "iw", LANGUAGE_iw, /* 20/115 Hebrew */ - "ka", LANGUAGE_ka_GE, /* 3/ 10 Georgian (Georgia) */ - "kk", LANGUAGE_kk, /* 15/112 Kazakh */ - "km", LANGUAGE_km_KH, /* 2/113 Khmer (Cambodia) */ - "ky", LANGUAGE_ky, /* 10/ 80 Kirghiz */ - "lo", LANGUAGE_lo_LA, /* 2/ 20 Lao (Laos) */ - "lt", LANGUAGE_lt, /* 18/ 22 Lithuanian */ - "lv", LANGUAGE_lv, /* 18/ 22 Latvian */ - "mk", LANGUAGE_mk, /* 9/ 85 Macedonian */ - "mn", LANGUAGE_mn_MN, /* 2/ 20 Mongolian (Mongolia) */ - "my", LANGUAGE_my_MM, /* 1/ 10 Burmese (Myanmar) */ - "nb", LANGUAGE_nb, /* 11/ 33 Norwegian Bokmål */ - "ne", LANGUAGE_ne_NP, /* 23/ 54 Nepali (Nepal) */ - "nl", LANGUAGE_nl, /* 9/ 12 Dutch */ - "pl", LANGUAGE_pl, /* 10/ 16 Polish */ - "pt", LANGUAGE_pt, /* 6/ 6 Portuguese */ - "rm", LANGUAGE_rm, /* 1/ 2 Raeto-Romance */ - "ro", LANGUAGE_ro, /* 6/ 15 Romanian */ - "ru", LANGUAGE_ru, /* 9/ 32 Russian */ - "sk", LANGUAGE_sk, /* 20/ 22 Slovak */ - "sl", LANGUAGE_sl, /* 8/ 19 Slovenian */ - "sr", LANGUAGE_sr, /* 11/ 85 Serbian */ - "sv", LANGUAGE_sv, /* 21/ 33 Swedish */ - "sw", LANGUAGE_sw, /* 9/ 17 Swahili */ - "th", LANGUAGE_th, /* 2/ 20 Thai */ - "tl", LANGUAGE_tl, /* 7/ 8 Tagalog */ - "tr", LANGUAGE_tr, /* 7/ 17 Turkish */ - "uk", LANGUAGE_uk, /* 11/ 79 Ukrainian */ - "vi", LANGUAGE_vi, /* 8/ 20 Vietnamese */ - "zu", LANGUAGE_zu, /* 8/ 11 Zulu */ - "zz", LANGUAGE_zz, /* 19/109 Alphabet */ + "DEFAULT", TEXTS_DEFAULT, /* 168/168 DEFAULT */ + "af" , TEXTS_af, /* 7/ 12 Afrikaans */ + "ar" , TEXTS_ar, /* 55/107 Arabic */ + "az_AZ" , TEXTS_az_AZ, /* 8/ 17 Azerbaijani (Azerbaijan) */ + "be_BY" , TEXTS_be_BY, /* 9/ 32 Belarusian (Belarus) */ + "bg" , TEXTS_bg, /* 2/ 10 Bulgarian */ + "ca" , TEXTS_ca, /* 11/120 Catalan */ + "cs" , TEXTS_cs, /* 17/ 21 Czech */ + "da" , TEXTS_da, /* 19/ 33 Danish */ + "de" , TEXTS_de, /* 16/ 60 German */ + "el" , TEXTS_el, /* 1/ 10 Greek */ + "en" , TEXTS_en, /* 8/ 11 English */ + "eo" , TEXTS_eo, /* 26/115 Esperanto */ + "es" , TEXTS_es, /* 8/ 61 Spanish */ + "et_EE" , TEXTS_et_EE, /* 22/ 27 Estonian (Estonia) */ + "eu_ES" , TEXTS_eu_ES, /* 7/ 8 Basque (Spain) */ + "fa" , TEXTS_fa, /* 58/123 Persian */ + "fi" , TEXTS_fi, /* 10/ 33 Finnish */ + "fr" , TEXTS_fr, /* 13/ 60 French */ + "gl_ES" , TEXTS_gl_ES, /* 7/ 8 Gallegan (Spain) */ + "hi" , TEXTS_hi, /* 23/ 54 Hindi */ + "hr" , TEXTS_hr, /* 9/ 19 Croatian */ + "hu" , TEXTS_hu, /* 9/ 19 Hungarian */ + "hy_AM" , TEXTS_hy_AM, /* 8/126 Armenian (Armenia) */ + "is" , TEXTS_is, /* 10/ 15 Icelandic */ + "it" , TEXTS_it, /* 11/ 60 Italian */ + "iw" , TEXTS_iw, /* 20/121 Hebrew */ + "ka_GE" , TEXTS_ka_GE, /* 3/ 10 Georgian (Georgia) */ + "kk" , TEXTS_kk, /* 15/118 Kazakh */ + "km_KH" , TEXTS_km_KH, /* 2/119 Khmer (Cambodia) */ + "ky" , TEXTS_ky, /* 10/ 86 Kirghiz */ + "lo_LA" , TEXTS_lo_LA, /* 2/ 20 Lao (Laos) */ + "lt" , TEXTS_lt, /* 18/ 22 Lithuanian */ + "lv" , TEXTS_lv, /* 18/ 22 Latvian */ + "mk" , TEXTS_mk, /* 9/ 91 Macedonian */ + "mn_MN" , TEXTS_mn_MN, /* 2/ 20 Mongolian (Mongolia) */ + "my_MM" , TEXTS_my_MM, /* 1/ 10 Burmese (Myanmar) */ + "nb" , TEXTS_nb, /* 11/ 33 Norwegian Bokmål */ + "ne_NP" , TEXTS_ne_NP, /* 23/ 54 Nepali (Nepal) */ + "nl" , TEXTS_nl, /* 9/ 12 Dutch */ + "pl" , TEXTS_pl, /* 10/ 16 Polish */ + "pt" , TEXTS_pt, /* 6/ 6 Portuguese */ + "rm" , TEXTS_rm, /* 1/ 2 Raeto-Romance */ + "ro" , TEXTS_ro, /* 6/ 15 Romanian */ + "ru" , TEXTS_ru, /* 9/ 32 Russian */ + "sk" , TEXTS_sk, /* 20/ 22 Slovak */ + "sl" , TEXTS_sl, /* 8/ 19 Slovenian */ + "sr" , TEXTS_sr, /* 11/ 91 Serbian */ + "sv" , TEXTS_sv, /* 21/ 33 Swedish */ + "sw" , TEXTS_sw, /* 9/ 17 Swahili */ + "th" , TEXTS_th, /* 2/ 20 Thai */ + "tl" , TEXTS_tl, /* 7/ 8 Tagalog */ + "tr" , TEXTS_tr, /* 7/ 17 Turkish */ + "uk" , TEXTS_uk, /* 11/ 85 Ukrainian */ + "vi" , TEXTS_vi, /* 8/ 20 Vietnamese */ + "zu" , TEXTS_zu, /* 8/ 11 Zulu */ + "zz" , TEXTS_zz, /* 19/109 Alphabet */ }; static { @@ -3711,11 +3736,11 @@ public final class KeyboardTextsTable { sNameToIndexesMap.put(NAMES[index], index); } - for (int i = 0; i < LANGUAGES_AND_TEXTS.length; i += 2) { - final String language = (String)LANGUAGES_AND_TEXTS[i]; - final String[] textsTable = (String[])LANGUAGES_AND_TEXTS[i + 1]; - sLanguageToTextsTableMap.put(language, textsTable); - sTextsTableToLanguageMap.put(textsTable, language); + for (int i = 0; i < LOCALES_AND_TEXTS.length; i += 2) { + final String locale = (String)LOCALES_AND_TEXTS[i]; + final String[] textsTable = (String[])LOCALES_AND_TEXTS[i + 1]; + sLocaleToTextsTableMap.put(locale, textsTable); + sTextsTableToLocaleMap.put(textsTable, locale); } } } diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 43a44221d..5e36d9703 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -89,6 +89,8 @@ public final class BinaryDictionary extends Dictionary { private final long mDictSize; private final String mDictFilePath; private final boolean mIsUpdatable; + private boolean mHasUpdated; + private final int[] mInputCodePoints = new int[MAX_WORD_LENGTH]; private final int[] mOutputSuggestionCount = new int[1]; private final int[] mOutputCodePoints = new int[MAX_WORD_LENGTH * MAX_RESULTS]; @@ -138,6 +140,7 @@ public final class BinaryDictionary extends Dictionary { mDictSize = length; mDictFilePath = filename; mIsUpdatable = isUpdatable; + mHasUpdated = false; mNativeSuggestOptions.setUseFullEditDistance(useFullEditDistance); loadDictionary(filename, offset, length, isUpdatable); } @@ -185,6 +188,7 @@ public final class BinaryDictionary extends Dictionary { // TODO: Move native dict into session private final void loadDictionary(final String path, final long startOffset, final long length, final boolean isUpdatable) { + mHasUpdated = false; mNativeDict = openNative(path, startOffset, length, isUpdatable); } @@ -243,24 +247,27 @@ public final class BinaryDictionary extends Dictionary { final String prevWord, final ProximityInfo proximityInfo, final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, final int sessionId, final float[] inOutLanguageWeight) { - if (!isValidDictionary()) return null; + if (!isValidDictionary()) { + return null; + } Arrays.fill(mInputCodePoints, Constants.NOT_A_CODE); // TODO: toLowerCase in the native code final int[] prevWordCodePointArray = (null == prevWord) ? null : StringUtils.toCodePointArray(prevWord); - final int composerSize = composer.sizeWithoutTrailingSingleQuotes(); - + final InputPointers inputPointers = composer.getInputPointers(); final boolean isGesture = composer.isBatchMode(); - if (composerSize <= 1 || !isGesture) { - if (composerSize > MAX_WORD_LENGTH - 1) return null; - for (int i = 0; i < composerSize; i++) { - mInputCodePoints[i] = composer.getCodeAt(i); + final int inputSize; + if (!isGesture) { + inputSize = composer.copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount( + mInputCodePoints, MAX_WORD_LENGTH); + if (inputSize < 0) { + return null; } + } else { + inputSize = inputPointers.getPointerSize(); } - final InputPointers ips = composer.getInputPointers(); - final int inputSize = isGesture ? ips.getPointerSize() : composerSize; mNativeSuggestOptions.setIsGesture(isGesture); mNativeSuggestOptions.setAdditionalFeaturesOptions(additionalFeaturesOptions); if (inOutLanguageWeight != null) { @@ -270,12 +277,12 @@ public final class BinaryDictionary extends Dictionary { } // proximityInfo and/or prevWordForBigrams may not be null. getSuggestionsNative(mNativeDict, proximityInfo.getNativeProximityInfo(), - getTraverseSession(sessionId).getSession(), ips.getXCoordinates(), - ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(), mInputCodePoints, - inputSize, mNativeSuggestOptions.getOptions(), - prevWordCodePointArray, mOutputSuggestionCount, mOutputCodePoints, mOutputScores, - mSpaceIndices, mOutputTypes, mOutputAutoCommitFirstWordConfidence, - mInputOutputLanguageWeight); + getTraverseSession(sessionId).getSession(), inputPointers.getXCoordinates(), + inputPointers.getYCoordinates(), inputPointers.getTimes(), + inputPointers.getPointerIds(), mInputCodePoints, inputSize, + mNativeSuggestOptions.getOptions(), prevWordCodePointArray, mOutputSuggestionCount, + mOutputCodePoints, mOutputScores, mSpaceIndices, mOutputTypes, + mOutputAutoCommitFirstWordConfidence, mInputOutputLanguageWeight); if (inOutLanguageWeight != null) { inOutLanguageWeight[0] = mInputOutputLanguageWeight[0]; } @@ -401,6 +408,7 @@ public final class BinaryDictionary extends Dictionary { StringUtils.toCodePointArray(shortcutTarget) : null; addUnigramWordNative(mNativeDict, codePoints, probability, shortcutTargetCodePoints, shortcutProbability, isNotAWord, isBlacklisted, timestamp); + mHasUpdated = true; } // Add a bigram entry to binary dictionary with timestamp in native code. @@ -412,6 +420,7 @@ public final class BinaryDictionary extends Dictionary { final int[] codePoints0 = StringUtils.toCodePointArray(word0); final int[] codePoints1 = StringUtils.toCodePointArray(word1); addBigramWordsNative(mNativeDict, codePoints0, codePoints1, probability, timestamp); + mHasUpdated = true; } // Remove a bigram entry form binary dictionary in native code. @@ -422,6 +431,7 @@ public final class BinaryDictionary extends Dictionary { final int[] codePoints0 = StringUtils.toCodePointArray(word0); final int[] codePoints1 = StringUtils.toCodePointArray(word1); removeBigramWordsNative(mNativeDict, codePoints0, codePoints1); + mHasUpdated = true; } public void addMultipleDictionaryEntries(final LanguageModelParam[] languageModelParams) { @@ -433,6 +443,7 @@ public final class BinaryDictionary extends Dictionary { } processedParamCount = addMultipleDictionaryEntriesNative(mNativeDict, languageModelParams, processedParamCount); + mHasUpdated = true; if (processedParamCount <= 0) { return; } @@ -451,8 +462,10 @@ public final class BinaryDictionary extends Dictionary { public void flush() { if (!isValidDictionary()) return; - flushNative(mNativeDict, mDictFilePath); - reopen(); + if (mHasUpdated) { + flushNative(mNativeDict, mDictFilePath); + reopen(); + } } public void flushWithGC() { diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java index 4e17f8389..d5873d70f 100644 --- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java @@ -100,10 +100,6 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { }); } - public void reopen(final Context context) { - registerObserver(context); - } - @Override public synchronized void close() { if (mObserver != null) { diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index aea4811a9..64e9d2b51 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -262,6 +262,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { ExecutorUtils.getExecutor(mDictName).execute(new Runnable() { @Override public void run() { + if (mBinaryDictionary == null) { + return; + } runGCAfterAllPrioritizedTasksIfRequiredLocked(mindsBlockByGC); } }); @@ -274,9 +277,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } private void runGCAfterAllPrioritizedTasksIfRequiredLocked(final boolean mindsBlockByGC) { - if (mBinaryDictionary == null) { - return; - } // needsToRunGC() have to be called with lock. if (mBinaryDictionary.needsToRunGC(mindsBlockByGC)) { if (setProcessingLargeTaskIfNot()) { @@ -301,9 +301,13 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { protected void addWordDynamically(final String word, final int frequency, final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord, final boolean isBlacklisted, final int timestamp) { + reloadDictionaryIfRequired(); ExecutorUtils.getExecutor(mDictName).execute(new Runnable() { @Override public void run() { + if (mBinaryDictionary == null) { + return; + } runGCAfterAllPrioritizedTasksIfRequiredLocked(true /* mindsBlockByGC */); addWordDynamicallyLocked(word, frequency, shortcutTarget, shortcutFreq, isNotAWord, isBlacklisted, timestamp); @@ -323,9 +327,13 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { */ protected void addBigramDynamically(final String word0, final String word1, final int frequency, final int timestamp) { + reloadDictionaryIfRequired(); ExecutorUtils.getExecutor(mDictName).execute(new Runnable() { @Override public void run() { + if (mBinaryDictionary == null) { + return; + } runGCAfterAllPrioritizedTasksIfRequiredLocked(true /* mindsBlockByGC */); addBigramDynamicallyLocked(word0, word1, frequency, timestamp); } @@ -341,9 +349,13 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { * Dynamically remove a word bigram in the dictionary. */ protected void removeBigramDynamically(final String word0, final String word1) { + reloadDictionaryIfRequired(); ExecutorUtils.getExecutor(mDictName).execute(new Runnable() { @Override public void run() { + if (mBinaryDictionary == null) { + return; + } runGCAfterAllPrioritizedTasksIfRequiredLocked(true /* mindsBlockByGC */); mBinaryDictionary.removeBigramWords(word0, word1); } @@ -360,14 +372,15 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { protected void addMultipleDictionaryEntriesDynamically( final ArrayList<LanguageModelParam> languageModelParams, final AddMultipleDictionaryEntriesCallback callback) { + reloadDictionaryIfRequired(); ExecutorUtils.getExecutor(mDictName).execute(new Runnable() { @Override public void run() { + if (mBinaryDictionary == null) { + return; + } final boolean locked = setProcessingLargeTaskIfNot(); try { - if (mBinaryDictionary == null) { - return; - } mBinaryDictionary.addMultipleDictionaryEntries( languageModelParams.toArray( new LanguageModelParam[languageModelParams.size()])); diff --git a/java/src/com/android/inputmethod/latin/ImportantNoticeDialog.java b/java/src/com/android/inputmethod/latin/ImportantNoticeDialog.java index 9870faa98..be54b669b 100644 --- a/java/src/com/android/inputmethod/latin/ImportantNoticeDialog.java +++ b/java/src/com/android/inputmethod/latin/ImportantNoticeDialog.java @@ -23,6 +23,7 @@ import android.content.DialogInterface.OnClickListener; import android.content.DialogInterface.OnDismissListener; import android.content.DialogInterface.OnShowListener; +import com.android.inputmethod.latin.utils.DialogUtils; import com.android.inputmethod.latin.utils.ImportantNoticeUtils; /** @@ -40,7 +41,7 @@ public final class ImportantNoticeDialog extends AlertDialog implements OnShowLi public ImportantNoticeDialog( final Context context, final ImportantNoticeDialogListener listener) { - super(context, THEME_HOLO_DARK); + super(DialogUtils.getPlatformDialogThemeContext(context)); mListener = listener; mNextImportantNoticeVersion = ImportantNoticeUtils.getNextImportantNoticeVersion(context); setMessage(ImportantNoticeUtils.getNextImportantNoticeContents(context)); diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 7ba7cc82a..0594c68cc 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -83,10 +83,12 @@ import com.android.inputmethod.latin.suggestions.SuggestionStripViewAccessor; import com.android.inputmethod.latin.utils.ApplicationUtils; import com.android.inputmethod.latin.utils.CapsModeUtils; import com.android.inputmethod.latin.utils.CoordinateUtils; +import com.android.inputmethod.latin.utils.DialogUtils; import com.android.inputmethod.latin.utils.ImportantNoticeUtils; import com.android.inputmethod.latin.utils.IntentUtils; import com.android.inputmethod.latin.utils.JniUtils; import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; +import com.android.inputmethod.latin.utils.StatsUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import com.android.inputmethod.research.ResearchLogger; @@ -509,6 +511,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen registerReceiver(mDictionaryDumpBroadcastReceiver, dictDumpFilter); DictionaryDecayBroadcastReciever.setUpIntervalAlarmForDictionaryDecaying(this); + + StatsUtils.onCreateCompleted(this); } // Has to be package-visible for unit tests @@ -628,9 +632,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen PersonalizationDictionarySessionRegistrar.close(this); LatinImeLogger.commit(); LatinImeLogger.onDestroy(); + StatsUtils.onDestroy(); super.onDestroy(); } + @UsedForTesting + public void recycle() { + mInputLogic.recycle(); + } + @Override public void onConfigurationChanged(final Configuration conf) { // If orientation changed while predicting, commit the change @@ -1667,8 +1677,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } }; - final AlertDialog.Builder builder = - new AlertDialog.Builder(this).setItems(items, listener).setTitle(title); + final AlertDialog.Builder builder = new AlertDialog.Builder( + DialogUtils.getPlatformDialogThemeContext(this)); + builder.setItems(items, listener).setTitle(title); showOptionDialog(builder.create()); } diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index f31fb134c..02f18cdd3 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -131,29 +131,42 @@ public final class WordComposer { return mCodePointSize; } - public boolean isSingleLetter() { - return size() == 1; + /** + * Copy the code points in the typed word to a destination array of ints. + * + * If the array is too small to hold the code points in the typed word, nothing is copied and + * -1 is returned. + * + * @param destination the array of ints. + * @param maxSize the size of the array. + * @return the number of copied code points. + */ + public int copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount( + final int[] destination, final int maxSize) { + int i = mTypedWordCache.length() - 1; + while (i >= 0 && mTypedWordCache.charAt(i) == Constants.CODE_SINGLE_QUOTE) { + --i; + } + if (i < 0) { + // The string is empty or contains only single quotes. + return 0; + } + final int codePointSize = Character.codePointCount(mTypedWordCache, 0, i); + if (codePointSize > maxSize) { + return -1; + } + return StringUtils.copyCodePointsAndReturnCodePointCount(destination, mTypedWordCache, 0, + i + 1, true /* downCase */); } - // When the composition contains trailing quotes, we don't pass them to the suggestion engine. - // This is because "'tgis'" should be corrected to "'this'", but we can't afford to consider - // single quotes as separators because of their very common use as apostrophes. - public int sizeWithoutTrailingSingleQuotes() { - return size() - mTrailingSingleQuotesCount; + public boolean isSingleLetter() { + return size() == 1; } public final boolean isComposingWord() { return size() > 0; } - // TODO: make sure that the index should not exceed MAX_WORD_LENGTH - public int getCodeAt(int index) { - if (index >= MAX_WORD_LENGTH) { - return -1; - } - return mPrimaryKeyCodes[index]; - } - public InputPointers getInputPointers() { return mInputPointers; } @@ -165,67 +178,61 @@ public final class WordComposer { } /** - * Add a new event for a key stroke, with the pressed key's code point with the touch point - * coordinates. + * Process an input event. + * + * All input events should be supported, including software/hardware events, characters as well + * as deletions, multiple inputs and gestures. + * + * @param event the event to process. */ - public void add(final Event event) { + public void processEvent(final Event event) { final int primaryCode = event.mCodePoint; final int keyX = event.mX; final int keyY = event.mY; final int newIndex = size(); - processEvent(event); - mCursorPositionWithinWord = mCodePointSize; - if (newIndex < MAX_WORD_LENGTH) { - mPrimaryKeyCodes[newIndex] = primaryCode >= Constants.CODE_SPACE - ? Character.toLowerCase(primaryCode) : primaryCode; - // In the batch input mode, the {@code mInputPointers} holds batch input points and - // shouldn't be overridden by the "typed key" coordinates - // (See {@link #setBatchInputWord}). - if (!mIsBatchMode) { - // TODO: Set correct pointer id and time - mInputPointers.addPointerAt(newIndex, keyX, keyY, 0, 0); - } - } - mIsFirstCharCapitalized = isFirstCharCapitalized( - newIndex, primaryCode, mIsFirstCharCapitalized); - if (Character.isUpperCase(primaryCode)) mCapsCount++; - if (Character.isDigit(primaryCode)) mDigitsCount++; - if (Constants.CODE_SINGLE_QUOTE == primaryCode) { - ++mTrailingSingleQuotesCount; - } else { - mTrailingSingleQuotesCount = 0; - } - mAutoCorrection = null; - } - - private void processEvent(final Event event) { mCombinerChain.processEvent(mEvents, event); mEvents.add(event); refreshTypedWordCache(); - } - - /** - * Delete the last composing unit as a result of hitting backspace. - */ - public void deleteLast(final Event event) { - processEvent(event); + mCursorPositionWithinWord = mCodePointSize; // We may have deleted the last one. - if (0 == size()) { + if (0 == mCodePointSize) { mIsFirstCharCapitalized = false; } - if (mTrailingSingleQuotesCount > 0) { - --mTrailingSingleQuotesCount; - } else { - int i = mTypedWordCache.length(); - while (i > 0) { - i = Character.offsetByCodePoints(mTypedWordCache, i, -1); - if (Constants.CODE_SINGLE_QUOTE != Character.codePointAt(mTypedWordCache, i)) { - break; + if (Constants.CODE_DELETE == event.mKeyCode) { + if (mTrailingSingleQuotesCount > 0) { + --mTrailingSingleQuotesCount; + } else { + // Delete, but we didn't end in a quote: must recompute mTrailingSingleQuotesCount + // We're only searching for single quotes, so no need to account for code points + for (int i = mTypedWordCache.length() - 1; i > 0; --i) { + if (Constants.CODE_SINGLE_QUOTE != mTypedWordCache.charAt(i)) { + break; + } + ++mTrailingSingleQuotesCount; } + } + } else { + if (Constants.CODE_SINGLE_QUOTE == primaryCode) { ++mTrailingSingleQuotesCount; + } else { + mTrailingSingleQuotesCount = 0; + } + if (newIndex < MAX_WORD_LENGTH) { + mPrimaryKeyCodes[newIndex] = primaryCode >= Constants.CODE_SPACE + ? Character.toLowerCase(primaryCode) : primaryCode; + // In the batch input mode, the {@code mInputPointers} holds batch input points and + // shouldn't be overridden by the "typed key" coordinates + // (See {@link #setBatchInputWord}). + if (!mIsBatchMode) { + // TODO: Set correct pointer id and time + mInputPointers.addPointerAt(newIndex, keyX, keyY, 0, 0); + } } + mIsFirstCharCapitalized = isFirstCharCapitalized( + newIndex, primaryCode, mIsFirstCharCapitalized); + if (Character.isUpperCase(primaryCode)) mCapsCount++; + if (Character.isDigit(primaryCode)) mDigitsCount++; } - mCursorPositionWithinWord = mCodePointSize; mAutoCorrection = null; } @@ -300,7 +307,7 @@ public final class WordComposer { final int codePoint = Character.codePointAt(word, i); // We don't want to override the batch input points that are held in mInputPointers // (See {@link #add(int,int,int)}). - add(Event.createEventForCodePointFromUnknownSource(codePoint)); + processEvent(Event.createEventForCodePointFromUnknownSource(codePoint)); } } @@ -317,7 +324,7 @@ public final class WordComposer { reset(); final int length = codePoints.length; for (int i = 0; i < length; ++i) { - add(Event.createEventForCodePointFromAlreadyTypedText(codePoints[i], + processEvent(Event.createEventForCodePointFromAlreadyTypedText(codePoints[i], CoordinateUtils.xFromArray(coordinates, i), CoordinateUtils.yFromArray(coordinates, i))); } diff --git a/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java b/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java index 800f56597..139e73aa4 100644 --- a/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java +++ b/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java @@ -28,6 +28,7 @@ import com.android.inputmethod.latin.BinaryDictionaryGetter; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.makedict.DictionaryHeader; import com.android.inputmethod.latin.utils.CollectionUtils; +import com.android.inputmethod.latin.utils.DialogUtils; import com.android.inputmethod.latin.utils.DictionaryInfoUtils; import com.android.inputmethod.latin.utils.LocaleUtils; @@ -70,7 +71,7 @@ public class ExternalDictionaryGetterForDebug { } private static void showNoFileDialog(final Context context) { - new AlertDialog.Builder(context) + new AlertDialog.Builder(DialogUtils.getPlatformDialogThemeContext(context)) .setMessage(R.string.read_external_dictionary_no_files_message) .setPositiveButton(android.R.string.ok, new OnClickListener() { @Override @@ -81,8 +82,8 @@ public class ExternalDictionaryGetterForDebug { } private static void showChooseFileDialog(final Context context, final String[] fileNames) { - final AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(R.string.read_external_dictionary_multiple_files_title) + new AlertDialog.Builder(DialogUtils.getPlatformDialogThemeContext(context)) + .setTitle(R.string.read_external_dictionary_multiple_files_title) .setItems(fileNames, new OnClickListener() { @Override public void onClick(final DialogInterface dialog, final int which) { @@ -111,7 +112,7 @@ public class ExternalDictionaryGetterForDebug { final String title = String.format( context.getString(R.string.read_external_dictionary_confirm_install_message), languageName); - new AlertDialog.Builder(context) + new AlertDialog.Builder(DialogUtils.getPlatformDialogThemeContext(context)) .setTitle(title) .setMessage(message) .setNegativeButton(android.R.string.cancel, new OnClickListener() { @@ -167,7 +168,7 @@ public class ExternalDictionaryGetterForDebug { } } catch (IOException e) { // There was an error: show a dialog - new AlertDialog.Builder(context) + new AlertDialog.Builder(DialogUtils.getPlatformDialogThemeContext(context)) .setTitle(R.string.error) .setMessage(e.toString()) .setPositiveButton(android.R.string.ok, new OnClickListener() { diff --git a/java/src/com/android/inputmethod/latin/define/ProductionFlag.java b/java/src/com/android/inputmethod/latin/define/ProductionFlag.java index e6fa1cdad..af899c040 100644 --- a/java/src/com/android/inputmethod/latin/define/ProductionFlag.java +++ b/java/src/com/android/inputmethod/latin/define/ProductionFlag.java @@ -30,6 +30,12 @@ public final class ProductionFlag { public static final boolean IS_HARDWARE_KEYBOARD_SUPPORTED = false; + // When true, enable {@link InputMethodService#onUpdateCursor} callback with + // {@link InputMethodService#setCursorAnchorMonitorMode}, which is not yet available in + // API level 19. Do not turn this on in production until the new API becomes publicly + // available. + public static final boolean USES_CURSOR_ANCHOR_MONITOR = false; + // Include all suggestions from all dictionaries in {@link SuggestedWords#mRawSuggestions}. public static final boolean INCLUDE_RAW_SUGGESTIONS = false; } diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index 30357943c..bf8467eb6 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -148,6 +148,17 @@ public final class InputLogic { mInputLogicHandler.reset(); } + // Normally this class just gets out of scope after the process ends, but in unit tests, we + // create several instances of LatinIME in the same process, which results in several + // instances of InputLogic. This cleans up the associated handler so that tests don't leak + // handlers. + public void recycle() { + final InputLogicHandler inputLogicHandler = mInputLogicHandler; + mInputLogicHandler = InputLogicHandler.NULL_HANDLER; + inputLogicHandler.destroy(); + mSuggest.mDictionaryFacilitator.closeDictionaries(); + } + /** * React to a string input. * @@ -729,7 +740,7 @@ public final class InputLogic { resetComposingState(false /* alsoResetLastComposedWord */); } if (isComposingWord) { - mWordComposer.add(inputTransaction.mEvent); + mWordComposer.processEvent(inputTransaction.mEvent); // If it's the first letter, make note of auto-caps state if (mWordComposer.isSingleLetter()) { // We pass 1 to getPreviousWordForSuggestion because we were not composing a word @@ -825,13 +836,11 @@ public final class InputLogic { } if (Constants.CODE_SPACE == codePoint) { - if (inputTransaction.mSettingsValues.isSuggestionsRequested()) { - if (maybeDoubleSpacePeriod(inputTransaction)) { - inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); - mSpaceState = SpaceState.DOUBLE; - } else if (!mSuggestedWords.isPunctuationSuggestions()) { - mSpaceState = SpaceState.WEAK; - } + if (maybeDoubleSpacePeriod(inputTransaction)) { + inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); + mSpaceState = SpaceState.DOUBLE; + } else if (!mSuggestedWords.isPunctuationSuggestions()) { + mSpaceState = SpaceState.WEAK; } startDoubleSpacePeriodCountdown(inputTransaction); @@ -897,7 +906,7 @@ public final class InputLogic { mWordComposer.reset(); mWordComposer.setRejectedBatchModeSuggestion(rejectedSuggestion); } else { - mWordComposer.deleteLast(inputTransaction.mEvent); + mWordComposer.processEvent(inputTransaction.mEvent); } mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1); inputTransaction.setRequiresUpdateSuggestions(); diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java index e3b8ab465..64bba681f 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java @@ -20,6 +20,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Message; +import com.android.inputmethod.compat.LooperCompatUtils; import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.Suggest; @@ -80,6 +81,12 @@ class InputLogicHandler implements Handler.Callback { mNonUIThreadHandler.removeCallbacksAndMessages(null); } + // In unit tests, we create several instances of LatinIME, which results in several instances + // of InputLogicHandler. To avoid these handlers lingering, we call this. + public void destroy() { + LooperCompatUtils.quitSafely(mNonUIThreadHandler.getLooper()); + } + /** * Handle a message. * @see android.os.Handler.Callback#handleMessage(android.os.Message) diff --git a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java index 6f84e1f10..712e314a8 100644 --- a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java +++ b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java @@ -66,7 +66,7 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB } // Flush pending writes. flush(); - // TODO: Quit depending on finalize() and really close the dictionary file. + super.close(); } public void flush() { diff --git a/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java b/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java index 6dae6206c..39977e76f 100644 --- a/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java +++ b/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java @@ -48,6 +48,7 @@ import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodManager; import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; import com.android.inputmethod.latin.utils.CollectionUtils; +import com.android.inputmethod.latin.utils.DialogUtils; import com.android.inputmethod.latin.utils.IntentUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; @@ -517,7 +518,8 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { private AlertDialog createDialog( @SuppressWarnings("unused") final SubtypePreference subtypePref) { - final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + final AlertDialog.Builder builder = new AlertDialog.Builder( + DialogUtils.getPlatformDialogThemeContext(getActivity())); builder.setTitle(R.string.custom_input_styles_title) .setMessage(R.string.custom_input_style_note_message) .setNegativeButton(R.string.not_now, null) diff --git a/java/src/com/android/inputmethod/latin/utils/DialogUtils.java b/java/src/com/android/inputmethod/latin/utils/DialogUtils.java new file mode 100644 index 000000000..a05c932d0 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/DialogUtils.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.utils; + +import android.content.Context; +import android.view.ContextThemeWrapper; + +import com.android.inputmethod.latin.R; + +public final class DialogUtils { + private DialogUtils() { + // This utility class is not publicly instantiable. + } + + public static Context getPlatformDialogThemeContext(final Context context) { + // Because {@link AlertDialog.Builder.create()} doesn't honor the specified theme with + // createThemeContextWrapper=false, the result dialog box has unneeded paddings around it. + return new ContextThemeWrapper(context, R.style.platformDialogTheme); + } +} diff --git a/java/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtils.java b/java/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtils.java index 89837c641..1ca895fdb 100644 --- a/java/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtils.java @@ -18,8 +18,6 @@ package com.android.inputmethod.latin.utils; import android.view.inputmethod.InputMethodSubtype; -import java.util.Locale; - public final class SpacebarLanguageUtils { private SpacebarLanguageUtils() { // Intentional empty constructor for utility class. @@ -55,7 +53,6 @@ public final class SpacebarLanguageUtils { if (SubtypeLocaleUtils.isNoLanguage(subtype)) { return SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype); } - final Locale locale = SubtypeLocaleUtils.getSubtypeLocale(subtype); - return SubtypeLocaleUtils.getSubtypeLocaleDisplayName(locale.getLanguage()); + return SubtypeLocaleUtils.getSubtypeLanguageDisplayName(subtype.getLocale()); } } diff --git a/java/src/com/android/inputmethod/latin/utils/StatsUtils.java b/java/src/com/android/inputmethod/latin/utils/StatsUtils.java new file mode 100644 index 000000000..a059f877b --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/StatsUtils.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.utils; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.util.Log; + +import com.android.inputmethod.latin.settings.Settings; + +public final class StatsUtils { + private static final String TAG = StatsUtils.class.getSimpleName(); + private static final StatsUtils sInstance = new StatsUtils(); + + public static void onCreateCompleted(final Context context) { + sInstance.onCreateCompletedInternal(context); + } + + private void onCreateCompletedInternal(final Context context) { + mContext = context; + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); + final Boolean usePersonalizedDict = + prefs.getBoolean(Settings.PREF_KEY_USE_PERSONALIZED_DICTS, true); + Log.d(TAG, "onCreateCompleted. context: " + context.toString() + "usePersonalizedDict: " + + usePersonalizedDict); + } + + public static void onDestroy() { + sInstance.onDestroyInternal(); + } + + private void onDestroyInternal() { + Log.d(TAG, "onDestroy. context: " + mContext.toString()); + mContext = null; + } + + private Context mContext; +} diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java index accbc8b7b..374badc19 100644 --- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java @@ -191,13 +191,42 @@ public final class StringUtils { } final int[] codePoints = new int[Character.codePointCount(charSequence, startIndex, endIndex)]; + copyCodePointsAndReturnCodePointCount(codePoints, charSequence, startIndex, endIndex, + false /* downCase */); + return codePoints; + } + + /** + * Copies the codepoints in a CharSequence to an int array. + * + * This method assumes there is enough space in the array to store the code points. The size + * can be measured with Character#codePointCount(CharSequence, int, int) before passing to this + * method. If the int array is too small, an ArrayIndexOutOfBoundsException will be thrown. + * Also, this method makes no effort to be thread-safe. Do not modify the CharSequence while + * this method is running, or the behavior is undefined. + * This method can optionally downcase code points before copying them, but it pays no attention + * to locale while doing so. + * + * @param destination the int array. + * @param charSequence the CharSequence. + * @param startIndex the start index inside the string in java chars, inclusive. + * @param endIndex the end index inside the string in java chars, exclusive. + * @param downCase if this is true, code points will be downcased before being copied. + * @return the number of copied code points. + */ + public static int copyCodePointsAndReturnCodePointCount(final int[] destination, + final CharSequence charSequence, final int startIndex, final int endIndex, + final boolean downCase) { int destIndex = 0; for (int index = startIndex; index < endIndex; index = Character.offsetByCodePoints(charSequence, index, 1)) { - codePoints[destIndex] = Character.codePointAt(charSequence, index); + final int codePoint = Character.codePointAt(charSequence, index); + // TODO: stop using this, as it's not aware of the locale and does not always do + // the right thing. + destination[destIndex] = downCase ? Character.toLowerCase(codePoint) : codePoint; destIndex++; } - return codePoints; + return destIndex; } public static int[] toSortedCodePointArray(final String string) { diff --git a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java index 2452864d5..b37779bdc 100644 --- a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java @@ -25,7 +25,7 @@ import android.os.Build; import android.util.Log; import android.view.inputmethod.InputMethodSubtype; -import com.android.inputmethod.latin.DictionaryFactory; +import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; import java.util.Arrays; @@ -33,10 +33,10 @@ import java.util.HashMap; import java.util.Locale; public final class SubtypeLocaleUtils { - static final String TAG = SubtypeLocaleUtils.class.getSimpleName(); - // This class must be located in the same package as LatinIME.java. - private static final String RESOURCE_PACKAGE_NAME = - DictionaryFactory.class.getPackage().getName(); + private static final String TAG = SubtypeLocaleUtils.class.getSimpleName(); + + // This reference class {@link Constants} must be located in the same package as LatinIME.java. + private static final String RESOURCE_PACKAGE_NAME = Constants.class.getPackage().getName(); // Special language code to represent "no language". public static final String NO_LANGUAGE = "zz"; @@ -44,7 +44,8 @@ public final class SubtypeLocaleUtils { public static final String EMOJI = "emoji"; public static final int UNKNOWN_KEYBOARD_LAYOUT = R.string.subtype_generic; - private static boolean sInitialized = false; + private static volatile boolean sInitialized = false; + private static final Object sInitializeLock = new Object(); private static Resources sResources; private static String[] sPredefinedKeyboardLayoutSet; // Keyboard layout to its display name map. @@ -77,9 +78,16 @@ public final class SubtypeLocaleUtils { } // Note that this initialization method can be called multiple times. - public static synchronized void init(final Context context) { - if (sInitialized) return; + public static void init(final Context context) { + synchronized (sInitializeLock) { + if (sInitialized == false) { + initLocked(context); + sInitialized = true; + } + } + } + private static void initLocked(final Context context) { final Resources res = context.getResources(); sResources = res; @@ -122,8 +130,6 @@ public final class SubtypeLocaleUtils { final String keyboardLayoutSet = keyboardLayoutSetMap[i + 1]; sLocaleAndExtraValueToKeyboardLayoutSetMap.put(key, keyboardLayoutSet); } - - sInitialized = true; } public static String[] getPredefinedKeyboardLayoutSet() { @@ -167,8 +173,18 @@ public final class SubtypeLocaleUtils { return getSubtypeLocaleDisplayNameInternal(localeString, displayLocale); } + public static String getSubtypeLanguageDisplayName(final String localeString) { + final Locale locale = LocaleUtils.constructLocaleFromString(localeString); + final Locale displayLocale = getDisplayLocaleOfSubtypeLocale(localeString); + return getSubtypeLocaleDisplayNameInternal(locale.getLanguage(), displayLocale); + } + private static String getSubtypeLocaleDisplayNameInternal(final String localeString, final Locale displayLocale) { + if (NO_LANGUAGE.equals(localeString)) { + // No language subtype should be displayed in system locale. + return sResources.getString(R.string.subtype_no_language); + } final Integer exceptionalNameResId = sExceptionalLocaleToNameIdsMap.get(localeString); final String displayName; if (exceptionalNameResId != null) { @@ -179,9 +195,6 @@ public final class SubtypeLocaleUtils { } }; displayName = getExceptionalName.runInLocale(sResources, displayLocale); - } else if (NO_LANGUAGE.equals(localeString)) { - // No language subtype should be displayed in system locale. - return sResources.getString(R.string.subtype_no_language); } else { final Locale locale = LocaleUtils.constructLocaleFromString(localeString); displayName = locale.getDisplayName(displayLocale); |