aboutsummaryrefslogtreecommitdiffstats
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java6
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DictionaryService.java28
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DownloadIdAndStartDate.java29
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java13
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java35
-rw-r--r--java/src/com/android/inputmethod/keyboard/TextDecorator.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java12
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java471
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java3
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFacilitator.java36
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java93
-rw-r--r--java/src/com/android/inputmethod/latin/NgramContext.java10
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java33
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java3
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java107
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/PrivateCommandPerformer.java40
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/NgramProperty.java26
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/WordProperty.java57
-rw-r--r--java/src/com/android/inputmethod/latin/settings/Settings.java2
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java5
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java129
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java6
-rw-r--r--java/src/com/android/inputmethod/latin/utils/CollectionUtils.java10
-rw-r--r--java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java7
24 files changed, 785 insertions, 378 deletions
diff --git a/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java b/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java
index c937eeeaa..5af31795c 100644
--- a/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java
@@ -84,8 +84,8 @@ public final class CursorAnchorInfoCompatWrapper {
}
@UsedForTesting
- public static boolean isAvailable() {
- return sCursorAnchorInfoClass.exists();
+ public boolean isAvailable() {
+ return sCursorAnchorInfoClass.exists() && mInstance != null;
}
private Object mInstance;
@@ -96,7 +96,7 @@ public final class CursorAnchorInfoCompatWrapper {
@UsedForTesting
public static CursorAnchorInfoCompatWrapper fromObject(final Object instance) {
- if (!isAvailable()) {
+ if (!sCursorAnchorInfoClass.exists()) {
return new CursorAnchorInfoCompatWrapper(null);
}
return new CursorAnchorInfoCompatWrapper(instance);
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java
index 41916b614..568c80abd 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java
@@ -76,19 +76,29 @@ public final class DictionaryService extends Service {
* How often, in milliseconds, we want to update the metadata. This is a
* floor value; actually, it may happen several hours later, or even more.
*/
- private static final long UPDATE_FREQUENCY = TimeUnit.DAYS.toMillis(4);
+ private static final long UPDATE_FREQUENCY_MILLIS = TimeUnit.DAYS.toMillis(4);
/**
* We are waked around midnight, local time. We want to wake between midnight and 6 am,
* roughly. So use a random time between 0 and this delay.
*/
- private static final int MAX_ALARM_DELAY = (int)TimeUnit.HOURS.toMillis(6);
+ private static final int MAX_ALARM_DELAY_MILLIS = (int)TimeUnit.HOURS.toMillis(6);
/**
* How long we consider a "very long time". If no update took place in this time,
* the content provider will trigger an update in the background.
*/
- private static final long VERY_LONG_TIME = TimeUnit.DAYS.toMillis(14);
+ private static final long VERY_LONG_TIME_MILLIS = TimeUnit.DAYS.toMillis(14);
+
+ /**
+ * After starting a download, how long we wait before considering it may be stuck. After this
+ * period is elapsed, if the keyboard tries to download again, then we cancel and re-register
+ * the request; if it's within this time, we just leave it be.
+ * It's important to note that we do not re-submit the request merely because the time is up.
+ * This is only to decide whether to cancel the old one and re-requesting when the keyboard
+ * fires a new request for the same data.
+ */
+ public static final long NO_CANCEL_DOWNLOAD_PERIOD_MILLIS = TimeUnit.SECONDS.toMillis(30);
/**
* An executor that serializes tasks given to it.
@@ -188,16 +198,16 @@ public final class DictionaryService extends Service {
*/
private static void checkTimeAndMaybeSetupUpdateAlarm(final Context context) {
// Of all clients, if the one that hasn't been updated for the longest
- // is still more recent than UPDATE_FREQUENCY, do nothing.
- if (!isLastUpdateAtLeastThisOld(context, UPDATE_FREQUENCY)) return;
+ // is still more recent than UPDATE_FREQUENCY_MILLIS, do nothing.
+ if (!isLastUpdateAtLeastThisOld(context, UPDATE_FREQUENCY_MILLIS)) return;
PrivateLog.log("Date changed - registering alarm");
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
- // Best effort to wake between midnight and MAX_ALARM_DELAY in the morning.
+ // Best effort to wake between midnight and MAX_ALARM_DELAY_MILLIS in the morning.
// It doesn't matter too much if this is very inexact.
final long now = System.currentTimeMillis();
- final long alarmTime = now + new Random().nextInt(MAX_ALARM_DELAY);
+ final long alarmTime = now + new Random().nextInt(MAX_ALARM_DELAY_MILLIS);
final Intent updateIntent = new Intent(DictionaryPackConstants.UPDATE_NOW_INTENT_ACTION);
final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
updateIntent, PendingIntent.FLAG_CANCEL_CURRENT);
@@ -223,11 +233,11 @@ public final class DictionaryService extends Service {
/**
* Refreshes data if it hasn't been refreshed in a very long time.
*
- * This will check the last update time, and if it's been more than VERY_LONG_TIME,
+ * This will check the last update time, and if it's been more than VERY_LONG_TIME_MILLIS,
* update metadata now - and possibly take subsequent update actions.
*/
public static void updateNowIfNotUpdatedInAVeryLongTime(final Context context) {
- if (!isLastUpdateAtLeastThisOld(context, VERY_LONG_TIME)) return;
+ if (!isLastUpdateAtLeastThisOld(context, VERY_LONG_TIME_MILLIS)) return;
UpdateHandler.tryUpdate(context, false);
}
diff --git a/java/src/com/android/inputmethod/dictionarypack/DownloadIdAndStartDate.java b/java/src/com/android/inputmethod/dictionarypack/DownloadIdAndStartDate.java
new file mode 100644
index 000000000..6247a15e2
--- /dev/null
+++ b/java/src/com/android/inputmethod/dictionarypack/DownloadIdAndStartDate.java
@@ -0,0 +1,29 @@
+/*
+ * 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.dictionarypack;
+
+/**
+ * A simple container of download ID and download start date.
+ */
+public class DownloadIdAndStartDate {
+ public final long mId;
+ public final long mStartDate;
+ public DownloadIdAndStartDate(final long id, final long startDate) {
+ mId = id;
+ mStartDate = startDate;
+ }
+}
diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java
index e9dde4245..c9e8f9118 100644
--- a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java
+++ b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java
@@ -433,18 +433,18 @@ public class MetadataDbHelper extends SQLiteOpenHelper {
*
* @param context a context instance to open the database on
* @param uri the URI to retrieve the metadata download ID of
- * @return the metadata download ID, or NOT_AN_ID if no download is in progress
+ * @return the download id and start date, or null if the URL is not known
*/
- public static long getMetadataDownloadIdForURI(final Context context,
- final String uri) {
+ public static DownloadIdAndStartDate getMetadataDownloadIdAndStartDateForURI(
+ final Context context, final String uri) {
SQLiteDatabase defaultDb = getDb(context, null);
final Cursor cursor = defaultDb.query(CLIENT_TABLE_NAME,
- new String[] { CLIENT_PENDINGID_COLUMN },
+ new String[] { CLIENT_PENDINGID_COLUMN, CLIENT_LAST_UPDATE_DATE_COLUMN },
CLIENT_METADATA_URI_COLUMN + " = ?", new String[] { uri },
null, null, null, null);
try {
- if (!cursor.moveToFirst()) return UpdateHandler.NOT_AN_ID;
- return cursor.getInt(0); // Only one column, return it
+ if (!cursor.moveToFirst()) return null;
+ return new DownloadIdAndStartDate(cursor.getInt(0), cursor.getLong(1));
} finally {
cursor.close();
}
@@ -922,6 +922,7 @@ public class MetadataDbHelper extends SQLiteOpenHelper {
final long downloadId) {
final ContentValues values = new ContentValues();
values.put(CLIENT_PENDINGID_COLUMN, downloadId);
+ values.put(CLIENT_LAST_UPDATE_DATE_COLUMN, System.currentTimeMillis());
final SQLiteDatabase defaultDb = getDb(context, "");
final Cursor cursor = MetadataDbHelper.queryClientIds(context);
if (null == cursor) return;
diff --git a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
index d8c5f3165..90a750493 100644
--- a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
+++ b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
@@ -251,12 +251,16 @@ public final class UpdateHandler {
res.getBoolean(R.bool.metadata_downloads_visible_in_download_UI));
final DownloadManagerWrapper manager = new DownloadManagerWrapper(context);
- cancelUpdateWithDownloadManager(context, metadataUri, manager);
+ if (maybeCancelUpdateAndReturnIfStillRunning(context, metadataUri, manager,
+ DictionaryService.NO_CANCEL_DOWNLOAD_PERIOD_MILLIS)) {
+ // We already have a recent download in progress. Don't register a new download.
+ return;
+ }
final long downloadId;
synchronized (sSharedIdProtector) {
downloadId = manager.enqueue(metadataRequest);
DebugLogUtils.l("Metadata download requested with id", downloadId);
- // If there is already a download in progress, it's been there for a while and
+ // If there is still a download in progress, it's been there for a while and
// there is probably something wrong with download manager. It's best to just
// overwrite the id and request it again. If the old one happens to finish
// anyway, we don't know about its ID any more, so the downloadFinished
@@ -267,21 +271,29 @@ public final class UpdateHandler {
}
/**
- * Cancels downloading a file, if there is one for this URI.
+ * Cancels downloading a file if there is one for this URI and it's too long.
*
* If we are not currently downloading the file at this URI, this is a no-op.
*
* @param context the context to open the database on
* @param metadataUri the URI to cancel
* @param manager an wrapped instance of DownloadManager
+ * @param graceTime if there was a download started less than this many milliseconds, don't
+ * cancel and return true
+ * @return whether the download is still active
*/
- private static void cancelUpdateWithDownloadManager(final Context context,
- final String metadataUri, final DownloadManagerWrapper manager) {
+ private static boolean maybeCancelUpdateAndReturnIfStillRunning(final Context context,
+ final String metadataUri, final DownloadManagerWrapper manager, final long graceTime) {
synchronized (sSharedIdProtector) {
- final long metadataDownloadId =
- MetadataDbHelper.getMetadataDownloadIdForURI(context, metadataUri);
- if (NOT_AN_ID == metadataDownloadId) return;
- manager.remove(metadataDownloadId);
+ final DownloadIdAndStartDate metadataDownloadIdAndStartDate =
+ MetadataDbHelper.getMetadataDownloadIdAndStartDateForURI(context, metadataUri);
+ if (null == metadataDownloadIdAndStartDate) return false;
+ if (NOT_AN_ID == metadataDownloadIdAndStartDate.mId) return false;
+ if (metadataDownloadIdAndStartDate.mStartDate + graceTime
+ > System.currentTimeMillis()) {
+ return true;
+ }
+ manager.remove(metadataDownloadIdAndStartDate.mId);
writeMetadataDownloadId(context, metadataUri, NOT_AN_ID);
}
// Consider a cancellation as a failure. As such, inform listeners that the download
@@ -289,6 +301,7 @@ public final class UpdateHandler {
for (UpdateEventListener listener : linkedCopyOfList(sUpdateEventListeners)) {
listener.downloadedMetadata(false);
}
+ return false;
}
/**
@@ -303,7 +316,7 @@ public final class UpdateHandler {
public static void cancelUpdate(final Context context, final String clientId) {
final DownloadManagerWrapper manager = new DownloadManagerWrapper(context);
final String metadataUri = MetadataDbHelper.getMetadataUriAsString(context, clientId);
- cancelUpdateWithDownloadManager(context, metadataUri, manager);
+ maybeCancelUpdateAndReturnIfStillRunning(context, metadataUri, manager, 0 /* graceTime */);
}
/**
@@ -387,7 +400,7 @@ public final class UpdateHandler {
// If any of these is metadata, we should update the DB
boolean hasMetadata = false;
for (DownloadRecord record : downloadRecords) {
- if (null == record.mAttributes) {
+ if (record.isMetadata()) {
hasMetadata = true;
break;
}
diff --git a/java/src/com/android/inputmethod/keyboard/TextDecorator.java b/java/src/com/android/inputmethod/keyboard/TextDecorator.java
index 6e4e3281e..c22717f95 100644
--- a/java/src/com/android/inputmethod/keyboard/TextDecorator.java
+++ b/java/src/com/android/inputmethod/keyboard/TextDecorator.java
@@ -182,7 +182,7 @@ public class TextDecorator {
private void layoutMain() {
final CursorAnchorInfoCompatWrapper info = mCursorAnchorInfoWrapper;
- if (info == null) {
+ if (info == null || !info.isAvailable()) {
cancelLayoutInternalExpectedly("CursorAnchorInfo isn't available.");
return;
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
index 1e1188bd0..71ce768a9 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
@@ -27,6 +27,9 @@ import java.util.Comparator;
import java.util.SortedSet;
import java.util.TreeSet;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
public class KeyboardParams {
public KeyboardId mId;
public int mThemeId;
@@ -67,7 +70,7 @@ public class KeyboardParams {
public final KeyboardTextsSet mTextsSet = new KeyboardTextsSet();
public final KeyStylesSet mKeyStyles = new KeyStylesSet(mTextsSet);
- public KeysCache mKeysCache;
+ @Nullable public KeysCache mKeysCache;
public boolean mAllowRedundantMoreKeys;
public int mMostCommonKeyHeight = 0;
@@ -96,7 +99,7 @@ public class KeyboardParams {
clearHistogram();
}
- public void onAddKey(final Key newKey) {
+ public void onAddKey(@Nonnull final Key newKey) {
final Key key = (mKeysCache != null) ? mKeysCache.get(newKey) : newKey;
final boolean isSpacer = key.isSpacer();
if (isSpacer && key.getWidth() == 0) {
@@ -129,7 +132,10 @@ public class KeyboardParams {
mSortedKeys.clear();
for (final Key key : allKeys) {
final Key filteredKey = Key.removeRedundantMoreKeys(key, lettersOnBaseLayout);
- mSortedKeys.add(mKeysCache.replace(key, filteredKey));
+ if (mKeysCache != null) {
+ mKeysCache.replace(key, filteredKey);
+ }
+ mSortedKeys.add(filteredKey);
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java
index a778ee80b..978194a3b 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java
@@ -83,24 +83,24 @@ public final class KeyboardTextsTable {
private static final String[] NAMES = {
// /* index:histogram */ "name",
- /* 0:32 */ "morekeys_a",
- /* 1:32 */ "morekeys_o",
- /* 2:31 */ "morekeys_e",
- /* 3:30 */ "morekeys_u",
- /* 4:30 */ "keylabel_to_alpha",
- /* 5:29 */ "morekeys_i",
- /* 6:24 */ "morekeys_n",
- /* 7:24 */ "morekeys_c",
+ /* 0:33 */ "morekeys_a",
+ /* 1:33 */ "morekeys_o",
+ /* 2:32 */ "morekeys_e",
+ /* 3:31 */ "morekeys_u",
+ /* 4:31 */ "keylabel_to_alpha",
+ /* 5:30 */ "morekeys_i",
+ /* 6:25 */ "morekeys_n",
+ /* 7:25 */ "morekeys_c",
/* 8:23 */ "double_quotes",
- /* 9:22 */ "single_quotes",
- /* 10:21 */ "morekeys_s",
- /* 11:18 */ "keyspec_currency",
- /* 12:16 */ "morekeys_y",
- /* 13:15 */ "morekeys_z",
+ /* 9:22 */ "morekeys_s",
+ /* 10:22 */ "single_quotes",
+ /* 11:19 */ "keyspec_currency",
+ /* 12:17 */ "morekeys_y",
+ /* 13:16 */ "morekeys_z",
/* 14:14 */ "morekeys_d",
/* 15:10 */ "morekeys_t",
/* 16:10 */ "morekeys_l",
- /* 17: 9 */ "morekeys_g",
+ /* 17:10 */ "morekeys_g",
/* 18: 9 */ "single_angle_quotes",
/* 19: 9 */ "double_angle_quotes",
/* 20: 8 */ "morekeys_r",
@@ -139,54 +139,54 @@ public final class KeyboardTextsTable {
/* 53: 4 */ "morekeys_nordic_row2_11",
/* 54: 4 */ "morekeys_punctuation",
/* 55: 4 */ "keyspec_tablet_comma",
- /* 56: 3 */ "keyspec_swiss_row1_11",
- /* 57: 3 */ "keyspec_swiss_row2_10",
- /* 58: 3 */ "keyspec_swiss_row2_11",
- /* 59: 3 */ "morekeys_swiss_row1_11",
- /* 60: 3 */ "morekeys_swiss_row2_10",
- /* 61: 3 */ "morekeys_swiss_row2_11",
- /* 62: 3 */ "morekeys_star",
- /* 63: 3 */ "keyspec_left_parenthesis",
- /* 64: 3 */ "keyspec_right_parenthesis",
- /* 65: 3 */ "keyspec_left_square_bracket",
- /* 66: 3 */ "keyspec_right_square_bracket",
- /* 67: 3 */ "keyspec_left_curly_bracket",
- /* 68: 3 */ "keyspec_right_curly_bracket",
- /* 69: 3 */ "keyspec_less_than",
- /* 70: 3 */ "keyspec_greater_than",
- /* 71: 3 */ "keyspec_less_than_equal",
- /* 72: 3 */ "keyspec_greater_than_equal",
- /* 73: 3 */ "keyspec_left_double_angle_quote",
- /* 74: 3 */ "keyspec_right_double_angle_quote",
- /* 75: 3 */ "keyspec_left_single_angle_quote",
- /* 76: 3 */ "keyspec_right_single_angle_quote",
- /* 77: 3 */ "keyspec_comma",
- /* 78: 3 */ "morekeys_tablet_comma",
- /* 79: 3 */ "keyhintlabel_period",
- /* 80: 3 */ "morekeys_tablet_period",
- /* 81: 3 */ "morekeys_question",
- /* 82: 2 */ "morekeys_h",
- /* 83: 2 */ "morekeys_w",
- /* 84: 2 */ "morekeys_east_slavic_row2_2",
- /* 85: 2 */ "morekeys_cyrillic_u",
- /* 86: 2 */ "morekeys_cyrillic_en",
- /* 87: 2 */ "morekeys_cyrillic_ghe",
- /* 88: 2 */ "morekeys_cyrillic_o",
- /* 89: 2 */ "morekeys_cyrillic_i",
- /* 90: 2 */ "keyspec_south_slavic_row1_6",
- /* 91: 2 */ "keyspec_south_slavic_row2_11",
- /* 92: 2 */ "keyspec_south_slavic_row3_1",
- /* 93: 2 */ "keyspec_south_slavic_row3_8",
- /* 94: 2 */ "morekeys_tablet_punctuation",
- /* 95: 2 */ "keyspec_spanish_row2_10",
- /* 96: 2 */ "morekeys_bullet",
- /* 97: 2 */ "morekeys_left_parenthesis",
- /* 98: 2 */ "morekeys_right_parenthesis",
- /* 99: 2 */ "morekeys_arabic_diacritics",
- /* 100: 2 */ "keyhintlabel_tablet_comma",
- /* 101: 2 */ "keyspec_period",
- /* 102: 2 */ "morekeys_period",
- /* 103: 2 */ "keyspec_tablet_period",
+ /* 56: 4 */ "morekeys_tablet_period",
+ /* 57: 3 */ "keyspec_swiss_row1_11",
+ /* 58: 3 */ "keyspec_swiss_row2_10",
+ /* 59: 3 */ "keyspec_swiss_row2_11",
+ /* 60: 3 */ "morekeys_swiss_row1_11",
+ /* 61: 3 */ "morekeys_swiss_row2_10",
+ /* 62: 3 */ "morekeys_swiss_row2_11",
+ /* 63: 3 */ "morekeys_star",
+ /* 64: 3 */ "keyspec_left_parenthesis",
+ /* 65: 3 */ "keyspec_right_parenthesis",
+ /* 66: 3 */ "keyspec_left_square_bracket",
+ /* 67: 3 */ "keyspec_right_square_bracket",
+ /* 68: 3 */ "keyspec_left_curly_bracket",
+ /* 69: 3 */ "keyspec_right_curly_bracket",
+ /* 70: 3 */ "keyspec_less_than",
+ /* 71: 3 */ "keyspec_greater_than",
+ /* 72: 3 */ "keyspec_less_than_equal",
+ /* 73: 3 */ "keyspec_greater_than_equal",
+ /* 74: 3 */ "keyspec_left_double_angle_quote",
+ /* 75: 3 */ "keyspec_right_double_angle_quote",
+ /* 76: 3 */ "keyspec_left_single_angle_quote",
+ /* 77: 3 */ "keyspec_right_single_angle_quote",
+ /* 78: 3 */ "keyspec_comma",
+ /* 79: 3 */ "morekeys_tablet_comma",
+ /* 80: 3 */ "keyspec_period",
+ /* 81: 3 */ "keyhintlabel_period",
+ /* 82: 3 */ "morekeys_period",
+ /* 83: 3 */ "keyspec_tablet_period",
+ /* 84: 3 */ "morekeys_question",
+ /* 85: 2 */ "morekeys_h",
+ /* 86: 2 */ "morekeys_w",
+ /* 87: 2 */ "morekeys_east_slavic_row2_2",
+ /* 88: 2 */ "morekeys_cyrillic_u",
+ /* 89: 2 */ "morekeys_cyrillic_en",
+ /* 90: 2 */ "morekeys_cyrillic_ghe",
+ /* 91: 2 */ "morekeys_cyrillic_o",
+ /* 92: 2 */ "morekeys_cyrillic_i",
+ /* 93: 2 */ "keyspec_south_slavic_row1_6",
+ /* 94: 2 */ "keyspec_south_slavic_row2_11",
+ /* 95: 2 */ "keyspec_south_slavic_row3_1",
+ /* 96: 2 */ "keyspec_south_slavic_row3_8",
+ /* 97: 2 */ "morekeys_tablet_punctuation",
+ /* 98: 2 */ "keyspec_spanish_row2_10",
+ /* 99: 2 */ "morekeys_bullet",
+ /* 100: 2 */ "morekeys_left_parenthesis",
+ /* 101: 2 */ "morekeys_right_parenthesis",
+ /* 102: 2 */ "morekeys_arabic_diacritics",
+ /* 103: 2 */ "keyhintlabel_tablet_comma",
/* 104: 2 */ "keyhintlabel_tablet_period",
/* 105: 2 */ "keyspec_symbols_question",
/* 106: 2 */ "keyspec_symbols_semicolon",
@@ -274,8 +274,8 @@ public final class KeyboardTextsTable {
EMPTY, EMPTY, EMPTY,
/* ~ morekeys_c */
/* double_quotes */ "!text/double_lqm_rqm",
- /* single_quotes */ "!text/single_lqm_rqm",
/* morekeys_s */ EMPTY,
+ /* single_quotes */ "!text/single_lqm_rqm",
/* keyspec_currency */ "$",
/* morekeys_y ~ */
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
@@ -302,6 +302,7 @@ public final class KeyboardTextsTable {
/* ~ morekeys_nordic_row2_11 */
/* morekeys_punctuation */ "!autoColumnOrder!8,\\,,?,!,#,!text/keyspec_right_parenthesis,!text/keyspec_left_parenthesis,/,;,',@,:,-,\",+,\\%,&",
/* keyspec_tablet_comma */ ",",
+ /* morekeys_tablet_period */ "!text/morekeys_tablet_punctuation",
/* keyspec_swiss_row1_11 ~ */
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
/* ~ morekeys_swiss_row2_11 */
@@ -334,8 +335,11 @@ public final class KeyboardTextsTable {
// Comma key
/* keyspec_comma */ ",",
/* morekeys_tablet_comma */ EMPTY,
+ // Period key
+ /* keyspec_period */ ".",
/* keyhintlabel_period */ EMPTY,
- /* morekeys_tablet_period */ "!text/morekeys_tablet_punctuation",
+ /* morekeys_period */ "!text/morekeys_punctuation",
+ /* keyspec_tablet_period */ ".",
// U+00BF: "¿" INVERTED QUESTION MARK
/* morekeys_question */ "\u00BF",
/* morekeys_h ~ */
@@ -352,13 +356,9 @@ public final class KeyboardTextsTable {
/* morekeys_bullet */ "\u266A,\u2665,\u2660,\u2666,\u2663",
/* morekeys_left_parenthesis */ "!fixedColumnOrder!3,!text/keyspecs_left_parenthesis_more_keys",
/* morekeys_right_parenthesis */ "!fixedColumnOrder!3,!text/keyspecs_right_parenthesis_more_keys",
- /* morekeys_arabic_diacritics */ EMPTY,
- /* keyhintlabel_tablet_comma */ EMPTY,
- // Period key
- /* keyspec_period */ ".",
- /* morekeys_period */ "!text/morekeys_punctuation",
- /* keyspec_tablet_period */ ".",
- /* keyhintlabel_tablet_period */ EMPTY,
+ /* morekeys_arabic_diacritics ~ */
+ EMPTY, EMPTY, EMPTY,
+ /* ~ keyhintlabel_tablet_period */
/* keyspec_symbols_question */ "?",
/* keyspec_symbols_semicolon */ ";",
/* keyspec_symbols_percent */ "%",
@@ -593,6 +593,7 @@ public final class KeyboardTextsTable {
// U+060C: "،" ARABIC COMMA
// U+061B: "؛" ARABIC SEMICOLON
/* keyspec_tablet_comma */ "\u060C",
+ /* morekeys_tablet_period */ "!text/morekeys_arabic_diacritics",
/* keyspec_swiss_row1_11 ~ */
null, null, null, null, null, null,
/* ~ morekeys_swiss_row2_11 */
@@ -622,9 +623,11 @@ public final class KeyboardTextsTable {
// U+060C: "،" ARABIC COMMA
/* keyspec_comma */ "\u060C",
/* morekeys_tablet_comma */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,\",\'",
+ /* keyspec_period */ null,
// U+0651: "ّ" ARABIC SHADDA
/* keyhintlabel_period */ "\u0651",
- /* morekeys_tablet_period */ "!text/morekeys_arabic_diacritics",
+ /* morekeys_period */ "!text/morekeys_arabic_diacritics",
+ /* keyspec_tablet_period */ null,
// U+00BF: "¿" INVERTED QUESTION MARK
/* morekeys_question */ "?,\u00BF",
/* morekeys_h ~ */
@@ -656,9 +659,6 @@ public final class KeyboardTextsTable {
// Note: The space character is needed as a preceding letter to draw Arabic diacritics characters correctly.
/* morekeys_arabic_diacritics */ "!fixedColumnOrder!7, \u0655|\u0655, \u0654|\u0654, \u0652|\u0652, \u064D|\u064D, \u064C|\u064C, \u064B|\u064B, \u0651|\u0651, \u0656|\u0656, \u0670|\u0670, \u0653|\u0653, \u0650|\u0650, \u064F|\u064F, \u064E|\u064E,\u0640\u0640\u0640|\u0640",
/* keyhintlabel_tablet_comma */ "\u061F",
- /* keyspec_period */ null,
- /* morekeys_period */ "!text/morekeys_arabic_diacritics",
- /* keyspec_tablet_period */ null,
/* keyhintlabel_tablet_period */ "\u0651",
/* keyspec_symbols_question */ "\u061F",
/* keyspec_symbols_semicolon */ "\u061B",
@@ -671,6 +671,7 @@ public final class KeyboardTextsTable {
/* Locale az_AZ: Azerbaijani (Azerbaijan) */
private static final String[] TEXTS_az_AZ = {
+ // This is the same as Turkish
// U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
// U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
// U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
@@ -710,12 +711,12 @@ public final class KeyboardTextsTable {
// U+010D: "č" LATIN SMALL LETTER C WITH CARON
/* morekeys_c */ "\u00E7,\u0107,\u010D",
/* double_quotes */ null,
- /* single_quotes */ null,
// U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
// U+00DF: "ß" LATIN SMALL LETTER SHARP S
// U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
// U+0161: "š" LATIN SMALL LETTER S WITH CARON
/* morekeys_s */ "\u015F,\u00DF,\u015B,\u0161",
+ /* single_quotes */ null,
/* keyspec_currency */ null,
// U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
/* morekeys_y */ "\u00FD",
@@ -742,9 +743,10 @@ public final class KeyboardTextsTable {
null, null, null,
/* ~ morekeys_c */
/* double_quotes */ "!text/double_9qm_lqm",
+ /* morekeys_s */ null,
/* single_quotes */ "!text/single_9qm_lqm",
- /* morekeys_s ~ */
- null, null, null, null, null, null, null, null, null, null, null, null,
+ /* keyspec_currency ~ */
+ null, null, null, null, null, null, null, null, null, null, null,
/* ~ morekeys_k */
// U+0451: "ё" CYRILLIC SMALL LETTER IO
/* morekeys_cyrillic_ie */ "\u0451",
@@ -780,6 +782,23 @@ public final class KeyboardTextsTable {
/* double_quotes */ "!text/double_9qm_lqm",
};
+ /* Locale bn_BD: Bengali (Bangladesh) */
+ private static final String[] TEXTS_bn_BD = {
+ /* morekeys_a ~ */
+ null, null, null, null,
+ /* ~ morekeys_u */
+ // Label for "switch to alphabetic" key.
+ // U+0995: "क" BENGALI LETTER KA
+ // U+0996: "ख" BENGALI LETTER KHA
+ // U+0997: "ग" BENGALI LETTER GA
+ /* keylabel_to_alpha */ "\u0995\u0996\u0997",
+ /* morekeys_i ~ */
+ null, null, null, null, null, null,
+ /* ~ single_quotes */
+ // U+09F3: "৳" BENGALI RUPEE SIGN
+ /* keyspec_currency */ "\u09F3",
+ };
+
/* Locale bn_IN: Bengali (India) */
private static final String[] TEXTS_bn_IN = {
/* morekeys_a ~ */
@@ -792,7 +811,7 @@ public final class KeyboardTextsTable {
/* keylabel_to_alpha */ "\u0995\u0996\u0997",
/* morekeys_i ~ */
null, null, null, null, null, null,
- /* ~ morekeys_s */
+ /* ~ single_quotes */
// U+20B9: "₹" INDIAN RUPEE SIGN
/* keyspec_currency */ "\u20B9",
};
@@ -865,7 +884,7 @@ public final class KeyboardTextsTable {
/* keyspec_tablet_comma ~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, 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 */
/* morekeys_tablet_punctuation */ "!autoColumnOrder!8,\\,,',\u00B7,#,),(,/,;,@,:,-,\",+,\\%,&",
// U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
@@ -925,11 +944,11 @@ public final class KeyboardTextsTable {
// U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
/* morekeys_c */ "\u010D,\u00E7,\u0107",
/* double_quotes */ "!text/double_9qm_lqm",
- /* single_quotes */ "!text/single_9qm_lqm",
// U+0161: "š" LATIN SMALL LETTER S WITH CARON
// U+00DF: "ß" LATIN SMALL LETTER SHARP S
// U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
/* morekeys_s */ "\u0161,\u00DF,\u015B",
+ /* single_quotes */ "!text/single_9qm_lqm",
/* keyspec_currency */ null,
// U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
// U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
@@ -988,11 +1007,11 @@ public final class KeyboardTextsTable {
/* morekeys_n */ "\u00F1,\u0144",
/* morekeys_c */ null,
/* double_quotes */ "!text/double_9qm_lqm",
- /* single_quotes */ "!text/single_9qm_lqm",
// U+00DF: "ß" LATIN SMALL LETTER SHARP S
// U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
// U+0161: "š" LATIN SMALL LETTER S WITH CARON
/* morekeys_s */ "\u00DF,\u015B,\u0161",
+ /* single_quotes */ "!text/single_9qm_lqm",
/* keyspec_currency */ null,
// U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
// U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
@@ -1064,11 +1083,11 @@ public final class KeyboardTextsTable {
/* morekeys_n */ "\u00F1,\u0144",
/* morekeys_c */ null,
/* double_quotes */ "!text/double_9qm_lqm",
- /* single_quotes */ "!text/single_9qm_lqm",
// U+00DF: "ß" LATIN SMALL LETTER SHARP S
// U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
// U+0161: "š" LATIN SMALL LETTER S WITH CARON
/* morekeys_s */ "\u00DF,\u015B,\u0161",
+ /* single_quotes */ "!text/single_9qm_lqm",
/* keyspec_currency ~ */
null, null, null, null, null, null, null,
/* ~ morekeys_g */
@@ -1077,8 +1096,8 @@ 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,
- /* ~ keyspec_tablet_comma */
+ null, null, null, null, null, null, null,
+ /* ~ morekeys_tablet_period */
// U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
/* keyspec_swiss_row1_11 */ "\u00FC",
// U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
@@ -1149,7 +1168,6 @@ public final class KeyboardTextsTable {
// U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
/* morekeys_c */ "\u00E7",
/* double_quotes */ null,
- /* single_quotes */ null,
// U+00DF: "ß" LATIN SMALL LETTER SHARP S
/* morekeys_s */ "\u00DF",
};
@@ -1223,13 +1241,13 @@ public final class KeyboardTextsTable {
// U+010B: "ċ" LATIN SMALL LETTER C WITH DOT ABOVE
/* morekeys_c */ "\u0107,\u010D,\u00E7,\u010B",
/* double_quotes */ null,
- /* single_quotes */ null,
// U+00DF: "ß" LATIN SMALL LETTER SHARP S
// U+0161: "š" LATIN SMALL LETTER S WITH CARON
// U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
// U+0219: "ș" LATIN SMALL LETTER S WITH COMMA BELOW
// U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
/* morekeys_s */ "\u00DF,\u0161,\u015B,\u0219,\u015F",
+ /* single_quotes */ null,
/* keyspec_currency */ null,
// U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
// U+0177: "ŷ" LATIN SMALL LETTER Y WITH CIRCUMFLEX
@@ -1273,6 +1291,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,
/* ~ morekeys_question */
// U+0125: "ĥ" LATIN SMALL LETTER H WITH CIRCUMFLEX
// U+0127: "ħ" LATIN SMALL LETTER H WITH STROKE
@@ -1286,7 +1305,7 @@ public final class KeyboardTextsTable {
/* keyspec_spanish_row2_10 */ "\u0135",
/* 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,
/* ~ label_wait_key */
// U+0175: "ŵ" LATIN SMALL LETTER W WITH CIRCUMFLEX
/* morekeys_v */ "w,\u0175",
@@ -1423,12 +1442,12 @@ public final class KeyboardTextsTable {
// U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
/* morekeys_c */ "\u010D,\u00E7,\u0107",
/* double_quotes */ "!text/double_9qm_lqm",
- /* single_quotes */ "!text/single_9qm_lqm",
// U+0161: "š" LATIN SMALL LETTER S WITH CARON
// U+00DF: "ß" LATIN SMALL LETTER SHARP S
// U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
// U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
/* morekeys_s */ "\u0161,\u00DF,\u015B,\u015F",
+ /* single_quotes */ "!text/single_9qm_lqm",
/* keyspec_currency */ null,
// U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
// U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
@@ -1536,7 +1555,7 @@ public final class KeyboardTextsTable {
/* keylabel_to_alpha */ "\u0627\u200C\u0628\u200C\u067E",
/* morekeys_i ~ */
null, null, null, null, null, null,
- /* ~ morekeys_s */
+ /* ~ single_quotes */
// U+FDFC: "﷼" RIAL SIGN
/* keyspec_currency */ "\uFDFC",
/* morekeys_y ~ */
@@ -1586,6 +1605,7 @@ public final class KeyboardTextsTable {
// U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
// U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
/* keyspec_tablet_comma */ "\u060C",
+ /* morekeys_tablet_period */ "!text/morekeys_arabic_diacritics",
/* keyspec_swiss_row1_11 ~ */
null, null, null, null, null, null,
/* ~ morekeys_swiss_row2_11 */
@@ -1609,9 +1629,11 @@ public final class KeyboardTextsTable {
// U+060C: "،" ARABIC COMMA
/* keyspec_comma */ "\u060C",
/* morekeys_tablet_comma */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,!text/keyspec_left_double_angle_quote,!text/keyspec_right_double_angle_quote",
+ /* keyspec_period */ null,
// U+064B: "ً" ARABIC FATHATAN
/* keyhintlabel_period */ "\u064B",
- /* morekeys_tablet_period */ "!text/morekeys_arabic_diacritics",
+ /* morekeys_period */ "!text/morekeys_arabic_diacritics",
+ /* keyspec_tablet_period */ null,
// U+00BF: "¿" INVERTED QUESTION MARK
/* morekeys_question */ "?,\u00BF",
/* morekeys_h ~ */
@@ -1643,9 +1665,6 @@ public final class KeyboardTextsTable {
// Note: The space character is needed as a preceding letter to draw Arabic diacritics characters correctly.
/* morekeys_arabic_diacritics */ "!fixedColumnOrder!7, \u0655|\u0655, \u0652|\u0652, \u0651|\u0651, \u064C|\u064C, \u064D|\u064D, \u064B|\u064B, \u0654|\u0654, \u0656|\u0656, \u0670|\u0670, \u0653|\u0653, \u064F|\u064F, \u0650|\u0650, \u064E|\u064E,\u0640\u0640\u0640|\u0640",
/* keyhintlabel_tablet_comma */ "\u061F",
- /* keyspec_period */ null,
- /* morekeys_period */ "!text/morekeys_arabic_diacritics",
- /* keyspec_tablet_period */ null,
/* keyhintlabel_tablet_period */ "\u064B",
/* keyspec_symbols_question */ "\u061F",
/* keyspec_symbols_semicolon */ "\u061B",
@@ -1692,14 +1711,15 @@ public final class KeyboardTextsTable {
// U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
/* morekeys_u */ "\u00FC",
/* keylabel_to_alpha ~ */
- null, null, null, null, null, null,
- /* ~ single_quotes */
+ null, null, null, null, null,
+ /* ~ double_quotes */
// U+0161: "š" LATIN SMALL LETTER S WITH CARON
// U+00DF: "ß" LATIN SMALL LETTER SHARP S
// U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
/* morekeys_s */ "\u0161,\u00DF,\u015B",
- /* keyspec_currency */ null,
- /* morekeys_y */ null,
+ /* single_quotes ~ */
+ null, null, null,
+ /* ~ morekeys_y */
// U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
// U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
// U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
@@ -1780,8 +1800,8 @@ public final class KeyboardTextsTable {
/* morekeys_z ~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, 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_tablet_comma */
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ /* ~ morekeys_tablet_period */
// U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
/* keyspec_swiss_row1_11 */ "\u00E8",
// U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
@@ -1862,7 +1882,7 @@ public final class KeyboardTextsTable {
/* keylabel_to_alpha */ "\u0915\u0916\u0917",
/* morekeys_i ~ */
null, null, null, null, null, null,
- /* ~ morekeys_s */
+ /* ~ single_quotes */
// U+20B9: "₹" INDIAN RUPEE SIGN
/* keyspec_currency */ "\u20B9",
/* morekeys_y ~ */
@@ -1901,13 +1921,26 @@ public final class KeyboardTextsTable {
/* additional_morekeys_symbols_8 */ "8",
/* additional_morekeys_symbols_9 */ "9",
/* additional_morekeys_symbols_0 */ "0",
+ /* morekeys_nordic_row2_11 ~ */
+ null, null, null,
+ /* ~ keyspec_tablet_comma */
+ /* morekeys_tablet_period */ "!autoColumnOrder!8,\\,,.,',#,),(,/,;,@,:,-,\",+,\\%,&",
+ /* keyspec_swiss_row1_11 ~ */
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null,
+ /* ~ morekeys_tablet_comma */
+ // U+0964: "।" DEVANAGARI DANDA
+ /* keyspec_period */ "\u0964",
+ /* keyhintlabel_period */ null,
+ /* morekeys_period */ "!autoColumnOrder!9,\\,,.,?,!,#,),(,/,;,',@,:,-,\",+,\\%,&",
+ /* keyspec_tablet_period */ "\u0964",
};
/* Locale hi_ZZ: Hindi (ZZ) */
private static final String[] TEXTS_hi_ZZ = {
/* morekeys_a ~ */
null, null, null, null, null, null, null, null, null, null, null,
- /* ~ morekeys_s */
+ /* ~ single_quotes */
// U+20B9: "₹" INDIAN RUPEE SIGN
/* keyspec_currency */ "\u20B9",
/* morekeys_y ~ */
@@ -1942,11 +1975,11 @@ public final class KeyboardTextsTable {
// U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
/* morekeys_c */ "\u010D,\u0107,\u00E7",
/* double_quotes */ "!text/double_9qm_rqm",
- /* single_quotes */ "!text/single_9qm_rqm",
// U+0161: "š" LATIN SMALL LETTER S WITH CARON
// U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
// U+00DF: "ß" LATIN SMALL LETTER SHARP S
/* morekeys_s */ "\u0161,\u015B,\u00DF",
+ /* single_quotes */ "!text/single_9qm_rqm",
/* keyspec_currency */ null,
/* morekeys_y */ null,
// U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
@@ -2009,9 +2042,10 @@ public final class KeyboardTextsTable {
/* morekeys_n */ null,
/* morekeys_c */ null,
/* double_quotes */ "!text/double_9qm_rqm",
+ /* morekeys_s */ null,
/* single_quotes */ "!text/single_9qm_rqm",
- /* morekeys_s ~ */
- null, null, null, null, null, null, null, null,
+ /* keyspec_currency ~ */
+ null, null, null, null, null, null, null,
/* ~ morekeys_g */
/* single_angle_quotes */ "!text/single_raqm_laqm",
/* double_angle_quotes */ "!text/double_raqm_laqm",
@@ -2045,6 +2079,7 @@ public final class KeyboardTextsTable {
// U+055F: "՟" ARMENIAN ABBREVIATION MARK
/* morekeys_punctuation */ "!autoColumnOrder!8,\\,,\u055E,\u055C,.,\u055A,\u0559,?,!,\u055D,\u055B,\u058A,\u00BB,\u00AB,\u055F,;,:",
/* keyspec_tablet_comma */ "\u055D",
+ /* morekeys_tablet_period */ "!text/morekeys_punctuation",
/* keyspec_swiss_row1_11 ~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null,
@@ -2056,22 +2091,19 @@ public final class KeyboardTextsTable {
// U+055D: "՝" ARMENIAN COMMA
/* keyspec_comma */ "\u055D",
/* morekeys_tablet_comma */ null,
+ // U+0589: "։" ARMENIAN FULL STOP
+ /* keyspec_period */ "\u0589",
/* keyhintlabel_period */ null,
- /* morekeys_tablet_period */ "!text/morekeys_punctuation",
+ /* morekeys_period */ null,
+ /* keyspec_tablet_period */ "\u0589",
// U+055E: "՞" ARMENIAN QUESTION MARK
// U+00BF: "¿" INVERTED QUESTION MARK
/* morekeys_question */ "\u055E,\u00BF",
/* morekeys_h ~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null,
- /* ~ keyhintlabel_tablet_comma */
- // U+0589: "։" ARMENIAN FULL STOP
- /* keyspec_period */ "\u0589",
- /* morekeys_period */ null,
- /* keyspec_tablet_period */ "\u0589",
- /* keyhintlabel_tablet_period ~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, 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_greater_than */
// U+055C: "՜" ARMENIAN EXCLAMATION MARK
// U+00A1: "¡" INVERTED EXCLAMATION MARK
@@ -2123,8 +2155,8 @@ public final class KeyboardTextsTable {
/* morekeys_n */ null,
/* morekeys_c */ null,
/* double_quotes */ "!text/double_9qm_lqm",
- /* single_quotes */ "!text/single_9qm_lqm",
/* morekeys_s */ null,
+ /* single_quotes */ "!text/single_9qm_lqm",
/* keyspec_currency */ null,
// U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
// U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
@@ -2184,8 +2216,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,
- /* ~ keyspec_tablet_comma */
+ null, null, null, null, null, null,
+ /* ~ morekeys_tablet_period */
// U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
/* keyspec_swiss_row1_11 */ "\u00FC",
// U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
@@ -2214,15 +2246,15 @@ public final class KeyboardTextsTable {
null, null, null,
/* ~ morekeys_c */
/* double_quotes */ "!text/double_rqm_9qm",
- /* single_quotes */ "!text/single_rqm_9qm",
/* morekeys_s */ null,
+ /* single_quotes */ "!text/single_rqm_9qm",
// U+20AA: "₪" NEW SHEQEL SIGN
/* keyspec_currency */ "\u20AA",
/* morekeys_y ~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, 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 */
// U+2605: "★" BLACK STAR
/* morekeys_star */ "\u2605",
@@ -2252,7 +2284,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,
/* ~ morekeys_currency_dollar */
// U+00B1: "±" PLUS-MINUS SIGN
// U+FB29: "﬩" HEBREW LETTER ALTERNATIVE PLUS SIGN
@@ -2273,6 +2305,7 @@ public final class KeyboardTextsTable {
null, null, null,
/* ~ morekeys_c */
/* double_quotes */ "!text/double_9qm_lqm",
+ /* morekeys_s */ null,
/* single_quotes */ "!text/single_9qm_lqm",
};
@@ -2309,7 +2342,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,
/* ~ morekeys_w */
// U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
/* morekeys_east_slavic_row2_2 */ "\u0456",
@@ -2325,7 +2358,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, null,
- null, null, null, null, null, null, null,
+ null, null, null, null,
/* ~ keyspec_x */
// U+04BB: "һ" CYRILLIC SMALL LETTER SHHA
/* morekeys_east_slavic_row2_11 */ "\u04BB",
@@ -2372,7 +2405,7 @@ public final class KeyboardTextsTable {
/* keylabel_to_alpha */ "\u0C85\u0C86\u0C87",
/* morekeys_i ~ */
null, null, null, null, null, null,
- /* ~ morekeys_s */
+ /* ~ single_quotes */
// U+20B9: "₹" INDIAN RUPEE SIGN
/* keyspec_currency */ "\u20B9",
};
@@ -2410,7 +2443,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, null,
/* ~ morekeys_east_slavic_row2_2 */
// U+04AF: "ү" CYRILLIC SMALL LETTER STRAIGHT U
/* morekeys_cyrillic_u */ "\u04AF",
@@ -2433,7 +2466,7 @@ public final class KeyboardTextsTable {
/* keylabel_to_alpha */ "\u0E81\u0E82\u0E84",
/* morekeys_i ~ */
null, null, null, null, null, null,
- /* ~ morekeys_s */
+ /* ~ single_quotes */
// U+20AD: "₭" KIP SIGN
/* keyspec_currency */ "\u20AD",
};
@@ -2496,12 +2529,12 @@ public final class KeyboardTextsTable {
// U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
/* morekeys_c */ "\u010D,\u00E7,\u0107",
/* double_quotes */ "!text/double_9qm_lqm",
- /* single_quotes */ "!text/single_9qm_lqm",
// U+0161: "š" LATIN SMALL LETTER S WITH CARON
// U+00DF: "ß" LATIN SMALL LETTER SHARP S
// U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
// U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
/* morekeys_s */ "\u0161,\u00DF,\u015B,\u015F",
+ /* single_quotes */ "!text/single_9qm_lqm",
/* keyspec_currency */ null,
// U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
// U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
@@ -2590,12 +2623,12 @@ public final class KeyboardTextsTable {
// U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
/* morekeys_c */ "\u010D,\u00E7,\u0107",
/* double_quotes */ "!text/double_9qm_lqm",
- /* single_quotes */ "!text/single_9qm_lqm",
// U+0161: "š" LATIN SMALL LETTER S WITH CARON
// U+00DF: "ß" LATIN SMALL LETTER SHARP S
// U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
// U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
/* morekeys_s */ "\u0161,\u00DF,\u015B,\u015F",
+ /* single_quotes */ "!text/single_9qm_lqm",
/* keyspec_currency */ null,
// U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
// U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
@@ -2641,9 +2674,10 @@ public final class KeyboardTextsTable {
null, null, null,
/* ~ morekeys_c */
/* double_quotes */ "!text/double_9qm_lqm",
+ /* morekeys_s */ null,
/* single_quotes */ "!text/single_9qm_lqm",
- /* morekeys_s ~ */
- null, null, null, null, null, null, null, null, null, null, null, null,
+ /* keyspec_currency ~ */
+ null, null, null, null, null, null, null, null, null, null, null,
/* ~ morekeys_k */
// U+0450: "ѐ" CYRILLIC SMALL LETTER IE WITH GRAVE
/* morekeys_cyrillic_ie */ "\u0450",
@@ -2652,7 +2686,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,
/* ~ morekeys_cyrillic_o */
// U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE
/* morekeys_cyrillic_i */ "\u045D",
@@ -2676,7 +2710,7 @@ public final class KeyboardTextsTable {
/* keylabel_to_alpha */ "\u0D05",
/* morekeys_i ~ */
null, null, null, null, null, null,
- /* ~ morekeys_s */
+ /* ~ single_quotes */
// U+20B9: "₹" INDIAN RUPEE SIGN
/* keyspec_currency */ "\u20B9",
};
@@ -2693,7 +2727,7 @@ public final class KeyboardTextsTable {
/* keylabel_to_alpha */ "\u0410\u0411\u0412",
/* morekeys_i ~ */
null, null, null, null, null, null,
- /* ~ morekeys_s */
+ /* ~ single_quotes */
// U+20AE: "₮" TUGRIK SIGN
/* keyspec_currency */ "\u20AE",
};
@@ -2710,7 +2744,7 @@ public final class KeyboardTextsTable {
/* keylabel_to_alpha */ "\u0915\u0916\u0917",
/* morekeys_i ~ */
null, null, null, null, null, null,
- /* ~ morekeys_s */
+ /* ~ single_quotes */
// U+20B9: "₹" INDIAN RUPEE SIGN
/* keyspec_currency */ "\u20B9",
/* morekeys_y ~ */
@@ -2771,22 +2805,19 @@ public final class KeyboardTextsTable {
// U+104A: "၊" MYANMAR SIGN LITTLE SECTION
// U+104B: "။" MYANMAR SIGN SECTION
/* keyspec_tablet_comma */ "\u104A",
- /* keyspec_swiss_row1_11 ~ */
+ /* morekeys_tablet_period ~ */
null, null, null, null, null, null, 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_comma */
/* morekeys_tablet_comma */ "\\,",
- /* keyhintlabel_period */ "\u104A",
- /* morekeys_tablet_period ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~ keyspec_south_slavic_row3_8 */
- /* morekeys_tablet_punctuation */ "!autoColumnOrder!8,.,',#,),(,/,;,@,...,:,-,\",+,\\%,&",
- /* keyspec_spanish_row2_10 ~ */
- null, null, null, null, null, null,
- /* ~ keyhintlabel_tablet_comma */
/* keyspec_period */ "\u104B",
+ /* keyhintlabel_period */ "\u104A",
/* morekeys_period */ null,
/* keyspec_tablet_period */ "\u104B",
+ /* morekeys_question ~ */
+ null, null, null, null, null, null, null, null, null, null, null, null, null,
+ /* ~ keyspec_south_slavic_row3_8 */
+ /* morekeys_tablet_punctuation */ "!autoColumnOrder!8,.,',#,),(,/,;,@,...,:,-,\",+,\\%,&",
};
/* Locale nb: Norwegian Bokmål */
@@ -2827,9 +2858,10 @@ public final class KeyboardTextsTable {
null, null, null, null,
/* ~ morekeys_c */
/* double_quotes */ "!text/double_9qm_rqm",
+ /* morekeys_s */ null,
/* single_quotes */ "!text/single_9qm_rqm",
- /* morekeys_s ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null,
+ /* keyspec_currency ~ */
+ null, null, null, null, null, null, null, null, null, null, null, null,
/* ~ morekeys_cyrillic_ie */
// U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
/* keyspec_nordic_row1_11 */ "\u00E5",
@@ -2859,7 +2891,7 @@ public final class KeyboardTextsTable {
/* keylabel_to_alpha */ "\u0915\u0916\u0917",
/* morekeys_i ~ */
null, null, null, null, null, null,
- /* ~ morekeys_s */
+ /* ~ single_quotes */
// U+0930/U+0941/U+002E "रु." NEPALESE RUPEE SIGN
/* keyspec_currency */ "\u0930\u0941.",
/* morekeys_y ~ */
@@ -2948,8 +2980,8 @@ public final class KeyboardTextsTable {
/* morekeys_n */ "\u00F1,\u0144",
/* morekeys_c */ null,
/* double_quotes */ "!text/double_9qm_rqm",
- /* single_quotes */ "!text/single_9qm_rqm",
/* morekeys_s */ null,
+ /* single_quotes */ "!text/single_9qm_rqm",
/* keyspec_currency */ null,
// U+0133: "ij" LATIN SMALL LIGATURE IJ
/* morekeys_y */ "\u0133",
@@ -2995,11 +3027,11 @@ public final class KeyboardTextsTable {
// U+010D: "č" LATIN SMALL LETTER C WITH CARON
/* morekeys_c */ "\u0107,\u00E7,\u010D",
/* double_quotes */ "!text/double_9qm_rqm",
- /* single_quotes */ "!text/single_9qm_rqm",
// U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
// U+00DF: "ß" LATIN SMALL LETTER SHARP S
// U+0161: "š" LATIN SMALL LETTER S WITH CARON
/* morekeys_s */ "\u015B,\u00DF,\u0161",
+ /* single_quotes */ "!text/single_9qm_rqm",
/* keyspec_currency */ null,
/* morekeys_y */ null,
// U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
@@ -3100,12 +3132,12 @@ public final class KeyboardTextsTable {
/* morekeys_n */ null,
/* morekeys_c */ null,
/* double_quotes */ "!text/double_9qm_rqm",
- /* single_quotes */ "!text/single_9qm_rqm",
// U+0219: "ș" LATIN SMALL LETTER S WITH COMMA BELOW
// U+00DF: "ß" LATIN SMALL LETTER SHARP S
// U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
// U+0161: "š" LATIN SMALL LETTER S WITH CARON
/* morekeys_s */ "\u0219,\u00DF,\u015B,\u0161",
+ /* single_quotes */ "!text/single_9qm_rqm",
/* keyspec_currency ~ */
null, null, null, null,
/* ~ morekeys_d */
@@ -3127,9 +3159,10 @@ public final class KeyboardTextsTable {
null, null, null,
/* ~ morekeys_c */
/* double_quotes */ "!text/double_9qm_lqm",
+ /* morekeys_s */ null,
/* single_quotes */ "!text/single_9qm_lqm",
- /* morekeys_s ~ */
- null, null, null, null, null, null, null, null, null, null, null, null,
+ /* keyspec_currency ~ */
+ null, null, null, null, null, null, null, null, null, null, null,
/* ~ morekeys_k */
// U+0451: "ё" CYRILLIC SMALL LETTER IO
/* morekeys_cyrillic_ie */ "\u0451",
@@ -3159,7 +3192,7 @@ public final class KeyboardTextsTable {
/* keylabel_to_alpha */ "\u0D85,\u0D86",
/* morekeys_i ~ */
null, null, null, null, null, null,
- /* ~ morekeys_s */
+ /* ~ single_quotes */
// U+0DBB/U+0DD4: "රු" SINHALA LETTER RAYANNA/SINHALA VOWEL SIGN KETTI PAA-PILLA
/* keyspec_currency */ "\u0DBB\u0DD4",
};
@@ -3222,12 +3255,12 @@ public final class KeyboardTextsTable {
// U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
/* morekeys_c */ "\u010D,\u00E7,\u0107",
/* double_quotes */ "!text/double_9qm_lqm",
- /* single_quotes */ "!text/single_9qm_lqm",
// U+0161: "š" LATIN SMALL LETTER S WITH CARON
// U+00DF: "ß" LATIN SMALL LETTER SHARP S
// U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
// U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
/* morekeys_s */ "\u0161,\u00DF,\u015B,\u015F",
+ /* single_quotes */ "!text/single_9qm_lqm",
/* keyspec_currency */ null,
// U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
// U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
@@ -3268,9 +3301,9 @@ public final class KeyboardTextsTable {
// U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
/* morekeys_c */ "\u010D,\u0107",
/* double_quotes */ "!text/double_9qm_lqm",
- /* single_quotes */ "!text/single_9qm_lqm",
// U+0161: "š" LATIN SMALL LETTER S WITH CARON
/* morekeys_s */ "\u0161",
+ /* single_quotes */ "!text/single_9qm_lqm",
/* keyspec_currency */ null,
/* morekeys_y */ null,
// U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
@@ -3299,9 +3332,10 @@ public final class KeyboardTextsTable {
null, null, null,
/* ~ morekeys_c */
/* double_quotes */ "!text/double_9qm_lqm",
+ /* morekeys_s */ null,
/* single_quotes */ "!text/single_9qm_lqm",
- /* morekeys_s ~ */
- null, null, null, null, null, null, null, null,
+ /* keyspec_currency ~ */
+ null, null, null, null, null, null, null,
/* ~ morekeys_g */
/* single_angle_quotes */ "!text/single_raqm_laqm",
/* double_angle_quotes */ "!text/double_raqm_laqm",
@@ -3314,7 +3348,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,
/* ~ morekeys_cyrillic_o */
// U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE
/* morekeys_cyrillic_i */ "\u045D",
@@ -3361,11 +3395,11 @@ public final class KeyboardTextsTable {
// U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
/* morekeys_c */ "\u010D,\u0107,%",
/* double_quotes */ null,
- /* single_quotes */ null,
// U+0161: "š" LATIN SMALL LETTER S WITH CARON
/* morekeys_s */ "\u0161,%",
- /* keyspec_currency */ null,
- /* morekeys_y */ null,
+ /* single_quotes ~ */
+ null, null, null,
+ /* ~ morekeys_y */
// U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
/* morekeys_z */ "\u017E,%",
// U+0111: "đ" LATIN SMALL LETTER D WITH STROKE
@@ -3436,12 +3470,12 @@ public final class KeyboardTextsTable {
// U+010D: "č" LATIN SMALL LETTER C WITH CARON
/* morekeys_c */ "\u00E7,\u0107,\u010D",
/* double_quotes */ null,
- /* single_quotes */ null,
// U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
// U+0161: "š" LATIN SMALL LETTER S WITH CARON
// U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
// U+00DF: "ß" LATIN SMALL LETTER SHARP S
/* morekeys_s */ "\u015B,\u0161,\u015F,\u00DF",
+ /* single_quotes */ null,
/* keyspec_currency */ null,
// U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
// U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
@@ -3527,11 +3561,10 @@ public final class KeyboardTextsTable {
// U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
/* morekeys_c */ "\u00E7",
/* double_quotes */ null,
- /* single_quotes */ null,
// U+00DF: "ß" LATIN SMALL LETTER SHARP S
/* morekeys_s */ "\u00DF",
- /* keyspec_currency ~ */
- null, null, null, null, null, null,
+ /* single_quotes ~ */
+ null, null, null, null, null, null, null,
/* ~ morekeys_l */
/* morekeys_g */ "g\'",
};
@@ -3548,7 +3581,7 @@ public final class KeyboardTextsTable {
/* keylabel_to_alpha */ "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD",
/* morekeys_i ~ */
null, null, null, null, null, null,
- /* ~ morekeys_s */
+ /* ~ single_quotes */
// U+0BF9: "௹" TAMIL RUPEE SIGN
/* keyspec_currency */ "\u0BF9",
};
@@ -3565,7 +3598,7 @@ public final class KeyboardTextsTable {
/* keylabel_to_alpha */ "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD",
/* morekeys_i ~ */
null, null, null, null, null, null,
- /* ~ morekeys_s */
+ /* ~ single_quotes */
// U+0DBB/U+0DD4: "රු" SINHALA LETTER RAYANNA/SINHALA VOWEL SIGN KETTI PAA-PILLA
/* keyspec_currency */ "\u0DBB\u0DD4",
};
@@ -3594,7 +3627,7 @@ public final class KeyboardTextsTable {
/* keylabel_to_alpha */ "\u0C05\u0C06\u0C07",
/* morekeys_i ~ */
null, null, null, null, null, null,
- /* ~ morekeys_s */
+ /* ~ single_quotes */
// U+20B9: "₹" INDIAN RUPEE SIGN
/* keyspec_currency */ "\u20B9",
};
@@ -3611,7 +3644,7 @@ public final class KeyboardTextsTable {
/* keylabel_to_alpha */ "\u0E01\u0E02\u0E04",
/* morekeys_i ~ */
null, null, null, null, null, null,
- /* ~ morekeys_s */
+ /* ~ single_quotes */
// U+0E3F: "฿" THAI CURRENCY SYMBOL BAHT
/* keyspec_currency */ "\u0E3F",
};
@@ -3711,12 +3744,12 @@ public final class KeyboardTextsTable {
// U+010D: "č" LATIN SMALL LETTER C WITH CARON
/* morekeys_c */ "\u00E7,\u0107,\u010D",
/* double_quotes */ null,
- /* single_quotes */ null,
// U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
// U+00DF: "ß" LATIN SMALL LETTER SHARP S
// U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
// U+0161: "š" LATIN SMALL LETTER S WITH CARON
/* morekeys_s */ "\u015F,\u00DF,\u015B,\u0161",
+ /* single_quotes */ null,
/* keyspec_currency */ null,
// U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
/* morekeys_y */ "\u00FD",
@@ -3743,8 +3776,8 @@ public final class KeyboardTextsTable {
null, null, null,
/* ~ morekeys_c */
/* double_quotes */ "!text/double_9qm_lqm",
- /* single_quotes */ "!text/single_9qm_lqm",
/* morekeys_s */ null,
+ /* single_quotes */ "!text/single_9qm_lqm",
// U+20B4: "₴" HRYVNIA SIGN
/* keyspec_currency */ "\u20B4",
/* morekeys_y ~ */
@@ -3764,7 +3797,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,
/* ~ morekeys_w */
// U+0457: "ї" CYRILLIC SMALL LETTER YI
/* morekeys_east_slavic_row2_2 */ "\u0457",
@@ -3774,6 +3807,66 @@ public final class KeyboardTextsTable {
/* morekeys_cyrillic_ghe */ "\u0491",
};
+ /* Locale uz_UZ: Uzbek (Uzbekistan) */
+ private static final String[] TEXTS_uz_UZ = {
+ // This is the same as Turkish
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ /* morekeys_a */ "\u00E2,\u00E4,\u00E1",
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ /* morekeys_o */ "\u00F6,\u00F4,\u0153,\u00F2,\u00F3,\u00F5,\u00F8,\u014D",
+ // U+0259: "ə" LATIN SMALL LETTER SCHWA
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ /* morekeys_e */ "\u0259,\u00E9",
+ // 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",
+ /* keylabel_to_alpha */ null,
+ // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ /* morekeys_i */ "\u0131,\u00EE,\u00EF,\u00EC,\u00ED,\u012F,\u012B",
+ // U+0148: "ň" LATIN SMALL LETTER N WITH CARON
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ /* morekeys_n */ "\u0148,\u00F1",
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ /* morekeys_c */ "\u00E7,\u0107,\u010D",
+ /* double_quotes */ null,
+ // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+ // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+ /* morekeys_s */ "\u015F,\u00DF,\u015B,\u0161",
+ /* single_quotes */ null,
+ /* keyspec_currency */ null,
+ // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
+ /* morekeys_y */ "\u00FD",
+ // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
+ /* morekeys_z */ "\u017E",
+ /* morekeys_d ~ */
+ null, null, null,
+ /* ~ morekeys_l */
+ // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE
+ /* morekeys_g */ "\u011F",
+ };
+
/* Locale vi: Vietnamese */
private static final String[] TEXTS_vi = {
// U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
@@ -3845,7 +3938,7 @@ public final class KeyboardTextsTable {
/* morekeys_i */ "\u00EC,\u00ED,\u1EC9,\u0129,\u1ECB",
/* morekeys_n ~ */
null, null, null, null, null,
- /* ~ morekeys_s */
+ /* ~ single_quotes */
// U+20AB: "₫" DONG SIGN
/* keyspec_currency */ "\u20AB",
// U+1EF3: "ỳ" LATIN SMALL LETTER Y WITH GRAVE
@@ -3904,7 +3997,6 @@ public final class KeyboardTextsTable {
// U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
/* morekeys_c */ "\u00E7",
/* double_quotes */ null,
- /* single_quotes */ null,
// U+00DF: "ß" LATIN SMALL LETTER SHARP S
/* morekeys_s */ "\u00DF",
};
@@ -3982,7 +4074,6 @@ public final class KeyboardTextsTable {
// U+010D: "č" LATIN SMALL LETTER C WITH CARON
/* morekeys_c */ "\u00E7,\u0107,\u0109,\u010B,\u010D",
/* double_quotes */ null,
- /* single_quotes */ null,
// U+00DF: "ß" LATIN SMALL LETTER SHARP S
// U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
// U+015D: "ŝ" LATIN SMALL LETTER S WITH CIRCUMFLEX
@@ -3990,6 +4081,7 @@ public final class KeyboardTextsTable {
// U+0161: "š" LATIN SMALL LETTER S WITH CARON
// U+017F: "ſ" LATIN SMALL LETTER LONG S
/* morekeys_s */ "\u00DF,\u015B,\u015D,\u015F,\u0161,\u017F",
+ /* single_quotes */ null,
/* keyspec_currency */ null,
// U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
// U+0177: "ŷ" LATIN SMALL LETTER Y WITH CIRCUMFLEX
@@ -4034,6 +4126,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,
/* ~ morekeys_question */
// U+0125: "ĥ" LATIN SMALL LETTER H WITH CIRCUMFLEX
/* morekeys_h */ "\u0125",
@@ -4042,7 +4135,7 @@ public final class KeyboardTextsTable {
/* 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,
/* ~ morekeys_v */
// U+0135: "ĵ" LATIN SMALL LETTER J WITH CIRCUMFLEX
/* morekeys_j */ "\u0135",
@@ -4056,42 +4149,43 @@ public final class KeyboardTextsTable {
"az_AZ" , TEXTS_az_AZ, /* 11/ 18 Azerbaijani (Azerbaijan) */
"be_BY" , TEXTS_be_BY, /* 9/ 32 Belarusian (Belarus) */
"bg" , TEXTS_bg, /* 2/ 9 Bulgarian */
+ "bn_BD" , TEXTS_bn_BD, /* 2/ 12 Bengali (Bangladesh) */
"bn_IN" , TEXTS_bn_IN, /* 2/ 12 Bengali (India) */
- "ca" , TEXTS_ca, /* 11/ 96 Catalan */
+ "ca" , TEXTS_ca, /* 11/ 99 Catalan */
"cs" , TEXTS_cs, /* 17/ 21 Czech */
"da" , TEXTS_da, /* 19/ 54 Danish */
- "de" , TEXTS_de, /* 16/ 62 German */
+ "de" , TEXTS_de, /* 16/ 63 German */
"el" , TEXTS_el, /* 1/ 5 Greek */
- "en" , TEXTS_en, /* 8/ 11 English */
+ "en" , TEXTS_en, /* 8/ 10 English */
"eo" , TEXTS_eo, /* 26/126 Esperanto */
"es" , TEXTS_es, /* 8/ 55 Spanish */
"et_EE" , TEXTS_et_EE, /* 22/ 27 Estonian (Estonia) */
"eu_ES" , TEXTS_eu_ES, /* 7/ 8 Basque (Spain) */
"fa" , TEXTS_fa, /* 58/133 Persian */
"fi" , TEXTS_fi, /* 10/ 54 Finnish */
- "fr" , TEXTS_fr, /* 13/ 62 French */
+ "fr" , TEXTS_fr, /* 13/ 63 French */
"gl_ES" , TEXTS_gl_ES, /* 7/ 8 Gallegan (Spain) */
- "hi" , TEXTS_hi, /* 23/ 53 Hindi */
+ "hi" , TEXTS_hi, /* 27/ 84 Hindi */
"hi_ZZ" , TEXTS_hi_ZZ, /* 9/118 Hindi (ZZ) */
"hr" , TEXTS_hr, /* 9/ 20 Croatian */
"hu" , TEXTS_hu, /* 9/ 20 Hungarian */
"hy_AM" , TEXTS_hy_AM, /* 9/134 Armenian (Armenia) */
"is" , TEXTS_is, /* 10/ 16 Icelandic */
- "it" , TEXTS_it, /* 11/ 62 Italian */
+ "it" , TEXTS_it, /* 11/ 63 Italian */
"iw" , TEXTS_iw, /* 20/131 Hebrew */
- "ka_GE" , TEXTS_ka_GE, /* 3/ 10 Georgian (Georgia) */
+ "ka_GE" , TEXTS_ka_GE, /* 3/ 11 Georgian (Georgia) */
"kk" , TEXTS_kk, /* 15/129 Kazakh */
"km_KH" , TEXTS_km_KH, /* 2/130 Khmer (Cambodia) */
"kn_IN" , TEXTS_kn_IN, /* 2/ 12 Kannada (India) */
- "ky" , TEXTS_ky, /* 10/ 89 Kirghiz */
+ "ky" , TEXTS_ky, /* 10/ 92 Kirghiz */
"lo_LA" , TEXTS_lo_LA, /* 2/ 12 Lao (Laos) */
"lt" , TEXTS_lt, /* 18/ 22 Lithuanian */
"lv" , TEXTS_lv, /* 18/ 22 Latvian */
- "mk" , TEXTS_mk, /* 9/ 94 Macedonian */
+ "mk" , TEXTS_mk, /* 9/ 97 Macedonian */
"ml_IN" , TEXTS_ml_IN, /* 2/ 12 Malayalam (India) */
"mn_MN" , TEXTS_mn_MN, /* 2/ 12 Mongolian (Mongolia) */
"mr_IN" , TEXTS_mr_IN, /* 23/ 53 Marathi (India) */
- "my_MM" , TEXTS_my_MM, /* 8/104 Burmese (Myanmar) */
+ "my_MM" , TEXTS_my_MM, /* 8/ 98 Burmese (Myanmar) */
"nb" , TEXTS_nb, /* 11/ 54 Norwegian Bokmål */
"ne_NP" , TEXTS_ne_NP, /* 23/ 53 Nepali (Nepal) */
"nl" , TEXTS_nl, /* 9/ 13 Dutch */
@@ -4103,7 +4197,7 @@ public final class KeyboardTextsTable {
"si_LK" , TEXTS_si_LK, /* 2/ 12 Sinhalese (Sri Lanka) */
"sk" , TEXTS_sk, /* 20/ 22 Slovak */
"sl" , TEXTS_sl, /* 8/ 20 Slovenian */
- "sr" , TEXTS_sr, /* 11/ 94 Serbian */
+ "sr" , TEXTS_sr, /* 11/ 97 Serbian */
"sr_ZZ" , TEXTS_sr_ZZ, /* 14/118 Serbian (ZZ) */
"sv" , TEXTS_sv, /* 21/ 54 Swedish */
"sw" , TEXTS_sw, /* 9/ 18 Swahili */
@@ -4114,9 +4208,10 @@ public final class KeyboardTextsTable {
"th" , TEXTS_th, /* 2/ 12 Thai */
"tl" , TEXTS_tl, /* 7/ 8 Tagalog */
"tr" , TEXTS_tr, /* 11/ 18 Turkish */
- "uk" , TEXTS_uk, /* 11/ 88 Ukrainian */
+ "uk" , TEXTS_uk, /* 11/ 91 Ukrainian */
+ "uz_UZ" , TEXTS_uz_UZ, /* 11/ 18 Uzbek (Uzbekistan) */
"vi" , TEXTS_vi, /* 8/ 15 Vietnamese */
- "zu" , TEXTS_zu, /* 8/ 11 Zulu */
+ "zu" , TEXTS_zu, /* 8/ 10 Zulu */
"zz" , TEXTS_zz, /* 19/120 Alphabet */
};
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 1da33ed3f..6f3c48c47 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -199,6 +199,9 @@ public final class BinaryDictionary extends Dictionary {
int[] word, int probability, int timestamp);
private static native boolean removeNgramEntryNative(long dict,
int[][] prevWordCodePointArrays, boolean[] isBeginningOfSentenceArray, int[] word);
+ private static native boolean updateCounterNative(long dict,
+ int[][] prevWordCodePointArrays, boolean[] isBeginningOfSentenceArray,
+ int[] word, boolean isValidWord, int count, int timestamp);
private static native int addMultipleDictionaryEntriesNative(long dict,
LanguageModelParam[] languageModelParams, int startIndex);
private static native String getPropertyNative(long dict, String query);
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
index 6dc1e8273..1f0317288 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
@@ -99,6 +99,30 @@ public class DictionaryFacilitator {
DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS.length);
/**
+ * Returns whether this facilitator is exactly for this list of locales.
+ * @param locales the list of locales to test against
+ * @return true if this facilitator handles exactly this list of locales, false otherwise
+ */
+ public boolean isForLocales(final Locale[] locales) {
+ if (locales.length != mDictionaryGroups.length) {
+ return false;
+ }
+ for (final Locale locale : locales) {
+ boolean found = false;
+ for (final DictionaryGroup group : mDictionaryGroups) {
+ if (locale.equals(group.mLocale)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* A group of dictionaries that work together for a single language.
*/
private static class DictionaryGroup {
@@ -199,6 +223,18 @@ public class DictionaryFacilitator {
return mDictionaryGroups[0].mLocale;
}
+ /**
+ * Returns the primary locale among all currently active locales. BE CAREFUL using this.
+ *
+ * DO NOT USE THIS just because it's convenient. Use it when it's correct, for example when
+ * choosing what dictionary to put a word in, or when changing the capitalization of a typed
+ * string.
+ * @return the primary active locale
+ */
+ public Locale getPrimaryLocale() {
+ return mDictionaryGroups[0].mLocale;
+ }
+
private static ExpandableBinaryDictionary getSubDict(final String dictType,
final Context context, final Locale locale, final File dictFile,
final String dictNamePrefix) {
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 5aae010ac..861a8903e 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -84,12 +84,12 @@ import com.android.inputmethod.latin.settings.SettingsActivity;
import com.android.inputmethod.latin.settings.SettingsValues;
import com.android.inputmethod.latin.suggestions.SuggestionStripView;
import com.android.inputmethod.latin.suggestions.SuggestionStripViewAccessor;
+import com.android.inputmethod.latin.touchinputconsumer.GestureConsumer;
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.CursorAnchorInfoUtils;
import com.android.inputmethod.latin.utils.DialogUtils;
-import com.android.inputmethod.latin.utils.DistracterFilterCheckingExactMatchesAndSuggestions;
import com.android.inputmethod.latin.utils.ImportantNoticeUtils;
import com.android.inputmethod.latin.utils.IntentUtils;
import com.android.inputmethod.latin.utils.JniUtils;
@@ -102,6 +102,7 @@ import com.android.inputmethod.latin.utils.ViewLayoutUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
@@ -177,6 +178,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private final boolean mIsHardwareAcceleratedDrawingEnabled;
+ private GestureConsumer mGestureConsumer = GestureConsumer.NULL_GESTURE_CONSUMER;
+
public final UIHandler mHandler = new UIHandler(this);
public static final class UIHandler extends LeakGuardHandlerWrapper<LatinIME> {
@@ -247,19 +250,20 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
case MSG_RESUME_SUGGESTIONS:
latinIme.mInputLogic.restartSuggestionsOnWordTouchedByCursor(
latinIme.mSettings.getCurrent(),
- msg.arg1 == ARG1_TRUE /* shouldIncludeResumedWordInSuggestions */,
latinIme.mKeyboardSwitcher.getCurrentKeyboardScriptId());
break;
case MSG_REOPEN_DICTIONARIES:
// We need to re-evaluate the currently composing word in case the script has
// changed.
postWaitForDictionaryLoad();
- latinIme.resetSuggest();
+ latinIme.resetDictionaryFacilitatorIfNecessary();
break;
case MSG_UPDATE_TAIL_BATCH_INPUT_COMPLETED:
+ final SuggestedWords suggestedWords = (SuggestedWords) msg.obj;
latinIme.mInputLogic.onUpdateTailBatchInputCompleted(
latinIme.mSettings.getCurrent(),
- (SuggestedWords) msg.obj, latinIme.mKeyboardSwitcher);
+ suggestedWords, latinIme.mKeyboardSwitcher);
+ latinIme.onTailBatchInputResultShown(suggestedWords);
break;
case MSG_RESET_CACHES:
final SettingsValues settingsValues = latinIme.mSettings.getCurrent();
@@ -288,8 +292,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
sendMessage(obtainMessage(MSG_REOPEN_DICTIONARIES));
}
- public void postResumeSuggestions(final boolean shouldIncludeResumedWordInSuggestions,
- final boolean shouldDelay) {
+ public void postResumeSuggestions(final boolean shouldDelay) {
final LatinIME latinIme = getOwnerInstance();
if (latinIme == null) {
return;
@@ -299,13 +302,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
removeMessages(MSG_RESUME_SUGGESTIONS);
if (shouldDelay) {
- sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS,
- shouldIncludeResumedWordInSuggestions ? ARG1_TRUE : ARG1_FALSE,
- 0 /* ignored */), mDelayInMillisecondsToUpdateSuggestions);
+ sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS),
+ mDelayInMillisecondsToUpdateSuggestions);
} else {
- sendMessage(obtainMessage(MSG_RESUME_SUGGESTIONS,
- shouldIncludeResumedWordInSuggestions ? ARG1_TRUE : ARG1_FALSE,
- 0 /* ignored */));
+ sendMessage(obtainMessage(MSG_RESUME_SUGGESTIONS));
}
}
@@ -542,9 +542,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mHandler.onCreate();
DEBUG = DebugFlags.DEBUG_ENABLED;
- // TODO: Resolve mutual dependencies of {@link #loadSettings()} and {@link #initSuggest()}.
+ // TODO: Resolve mutual dependencies of {@link #loadSettings()} and
+ // {@link #resetDictionaryFacilitatorIfNecessary()}.
loadSettings();
- resetSuggest();
+ resetDictionaryFacilitatorIfNecessary();
// Register to receive ringer mode change and network state change.
// Also receive installation and removal of a dictionary pack.
@@ -585,7 +586,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// been displayed. Opening dictionaries never affects responsivity as dictionaries are
// asynchronously loaded.
if (!mHandler.hasPendingReopenDictionaries()) {
- resetSuggestForLocale(locale);
+ resetDictionaryFacilitatorForLocale(locale);
}
mDictionaryFacilitator.updateEnabledSubtypes(mRichImm.getMyEnabledInputMethodSubtypeList(
true /* allowsImplicitlySelectedSubtypes */));
@@ -622,13 +623,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
if (mHandler.hasPendingWaitForDictionaryLoad()) {
mHandler.cancelWaitForDictionaryLoad();
- mHandler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */,
- false /* shouldDelay */);
+ mHandler.postResumeSuggestions(false /* shouldDelay */);
}
}
- private void resetSuggest() {
+ private void resetDictionaryFacilitatorIfNecessary() {
final Locale switcherSubtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
+ if (mDictionaryFacilitator.isForLocales(new Locale[] { switcherSubtypeLocale })) {
+ return;
+ }
final String switcherLocaleStr = switcherSubtypeLocale.toString();
final Locale subtypeLocale;
if (TextUtils.isEmpty(switcherLocaleStr)) {
@@ -643,15 +646,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} else {
subtypeLocale = switcherSubtypeLocale;
}
- resetSuggestForLocale(subtypeLocale);
+ resetDictionaryFacilitatorForLocale(subtypeLocale);
}
/**
- * Reset suggest by loading dictionaries for the locale and the current settings values.
+ * Reset the facilitator by loading dictionaries for the locale and the current settings values.
*
* @param locale the locale
*/
- private void resetSuggestForLocale(final Locale locale) {
+ // TODO: make sure the current settings always have the right locale, and read from them
+ private void resetDictionaryFacilitatorForLocale(final Locale locale) {
final SettingsValues settingsValues = mSettings.getCurrent();
mDictionaryFacilitator.resetDictionaries(this /* context */, locale,
settingsValues.mUseContactsDict, settingsValues.mUsePersonalizedDicts,
@@ -801,6 +805,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
StatsUtils.onFinishInputView();
mHandler.onFinishInputView(finishingInput);
mStatsUtilsManager.onFinishInputView();
+ mGestureConsumer = GestureConsumer.NULL_GESTURE_CONSUMER;
}
@Override
@@ -826,6 +831,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@SuppressWarnings("deprecation")
private void onStartInputViewInternal(final EditorInfo editorInfo, final boolean restarting) {
super.onStartInputView(editorInfo, restarting);
+ // Switch to the null consumer to handle cases leading to early exit below, for which we
+ // also wouldn't be consuming gesture data.
+ mGestureConsumer = GestureConsumer.NULL_GESTURE_CONSUMER;
mRichImm.clearSubtypeCaches();
final KeyboardSwitcher switcher = mKeyboardSwitcher;
switcher.updateKeyboardTheme();
@@ -869,6 +877,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return;
}
+ // Update to a gesture consumer with the current editor and IME state.
+ mGestureConsumer = GestureConsumer.newInstance(editorInfo,
+ mInputLogic.getPrivateCommandPerformer(),
+ Collections.singletonList(mSubtypeSwitcher.getCurrentSubtypeLocale()),
+ switcher.getKeyboard());
+
// Forward this event to the accessibility utilities, if enabled.
final AccessibilityUtils accessUtils = AccessibilityUtils.getInstance();
if (accessUtils.isTouchExplorationEnabled()) {
@@ -907,12 +921,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mInputLogic.startInput(mSubtypeSwitcher.getCombiningRulesExtraValueOfCurrentSubtype(),
currentSettingsValues);
- // Note: the following does a round-trip IPC on the main thread: be careful
- final Locale currentLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
- if (null != currentLocale && !currentLocale.equals(suggest.getLocale())) {
- // TODO: Do this automatically.
- resetSuggest();
- }
+ resetDictionaryFacilitatorIfNecessary();
// TODO[IL]: Can the following be moved to InputLogic#startInput?
if (!mInputLogic.mConnection.resetCachesUponCursorMoveAndReturnSuccess(
@@ -926,11 +935,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// mLastSelection{Start,End} are reset later in this method, no need to do it here
needToCallLoadKeyboardLater = true;
} else {
- // When rotating, initialSelStart and initialSelEnd sometimes are lying. Make a best
- // effort to work around this bug.
+ // When rotating, and when input is starting again in a field from where the focus
+ // didn't move (the keyboard having been closed with the back key),
+ // initialSelStart and initialSelEnd sometimes are lying. Make a best effort to
+ // work around this bug.
mInputLogic.mConnection.tryFixLyingCursorPosition();
- mHandler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */,
- true /* shouldDelay */);
+ mHandler.postResumeSuggestions(true /* shouldDelay */);
needToCallLoadKeyboardLater = false;
}
} else {
@@ -1403,6 +1413,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override
public void onStartBatchInput() {
mInputLogic.onStartBatchInput(mSettings.getCurrent(), mKeyboardSwitcher, mHandler);
+ mGestureConsumer.onGestureStarted(
+ Collections.singletonList(mSubtypeSwitcher.getCurrentSubtypeLocale()),
+ mKeyboardSwitcher.getKeyboard());
}
@Override
@@ -1413,11 +1426,25 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override
public void onEndBatchInput(final InputPointers batchPointers) {
mInputLogic.onEndBatchInput(batchPointers);
+ mGestureConsumer.onGestureCompleted(batchPointers);
}
@Override
public void onCancelBatchInput() {
mInputLogic.onCancelBatchInput(mHandler);
+ mGestureConsumer.onGestureCanceled();
+ }
+
+ /**
+ * To be called after the InputLogic has gotten a chance to act on the on-device decoding
+ * for the full gesture, possibly updating the TextView to reflect the first decoding.
+ * <p>
+ * This method must be run on the UI Thread.
+ * @param suggestedWords On-device decoding for the full gesture.
+ */
+ public void onTailBatchInputResultShown(final SuggestedWords suggestedWords) {
+ mGestureConsumer.onImeSuggestionsProcessed(suggestedWords,
+ mInputLogic.getComposingStart(), mInputLogic.getComposingLength());
}
// This method must run on the UI Thread.
@@ -1559,7 +1586,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
final String wordToShow;
if (CapsModeUtils.isAutoCapsMode(mInputLogic.mLastComposedWord.mCapitalizedMode)) {
- wordToShow = word.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale());
+ wordToShow = word.toLowerCase(mDictionaryFacilitator.getPrimaryLocale());
} else {
wordToShow = word;
}
@@ -1845,7 +1872,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
public void dumpDictionaryForDebug(final String dictName) {
if (mDictionaryFacilitator.getLocale() == null) {
- resetSuggest();
+ resetDictionaryFacilitatorIfNecessary();
}
mDictionaryFacilitator.dumpDictionaryForDebug(dictName);
}
diff --git a/java/src/com/android/inputmethod/latin/NgramContext.java b/java/src/com/android/inputmethod/latin/NgramContext.java
index c35c6e2c8..6d438584f 100644
--- a/java/src/com/android/inputmethod/latin/NgramContext.java
+++ b/java/src/com/android/inputmethod/latin/NgramContext.java
@@ -169,8 +169,14 @@ public class NgramContext {
@Override
public int hashCode() {
- // Just for having equals().
- return mPrevWordsInfo[0].hashCode();
+ int hashValue = 0;
+ for (final WordInfo wordInfo : mPrevWordsInfo) {
+ if (wordInfo == null || !WordInfo.EMPTY_WORD_INFO.equals(wordInfo)) {
+ break;
+ }
+ hashValue ^= wordInfo.hashCode();
+ }
+ return hashValue;
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 750706113..62a258b20 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -19,6 +19,7 @@ package com.android.inputmethod.latin;
import android.graphics.Color;
import android.inputmethodservice.InputMethodService;
import android.os.Build;
+import android.os.Bundle;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
@@ -33,6 +34,7 @@ import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import com.android.inputmethod.compat.InputConnectionCompatUtils;
+import com.android.inputmethod.latin.inputlogic.PrivateCommandPerformer;
import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
import com.android.inputmethod.latin.utils.CapsModeUtils;
import com.android.inputmethod.latin.utils.DebugLogUtils;
@@ -52,7 +54,7 @@ import java.util.Arrays;
* all the time to find out what text is in the buffer, when we need it to determine caps mode
* for example.
*/
-public final class RichInputConnection {
+public final class RichInputConnection implements PrivateCommandPerformer {
private static final String TAG = RichInputConnection.class.getSimpleName();
private static final boolean DBG = false;
private static final boolean DEBUG_PREVIOUS_TEXT = false;
@@ -849,8 +851,9 @@ public final class RichInputConnection {
/**
* Try to get the text from the editor to expose lies the framework may have been
- * telling us. Concretely, when the device rotates, the frameworks tells us about where the
- * cursor used to be initially in the editor at the time it first received the focus; this
+ * telling us. Concretely, when the device rotates and when the keyboard reopens in the same
+ * text field after having been closed with the back key, the frameworks tells us about where
+ * the cursor used to be initially in the editor at the time it first received the focus; this
* may be completely different from the place it is upon rotation. Since we don't have any
* means to get the real value, try at least to ask the text view for some characters and
* detect the most damaging cases: when the cursor position is declared to be much smaller
@@ -859,7 +862,20 @@ public final class RichInputConnection {
public void tryFixLyingCursorPosition() {
final CharSequence textBeforeCursor = getTextBeforeCursor(
Constants.EDITOR_CONTENTS_CACHE_SIZE, 0);
- if (null == textBeforeCursor) {
+ final CharSequence selectedText = mIC.getSelectedText(0 /* flags */);
+ if (null == textBeforeCursor ||
+ (!TextUtils.isEmpty(selectedText) && mExpectedSelEnd == mExpectedSelStart)) {
+ // If textBeforeCursor is null, we have no idea what kind of text field we have or if
+ // thinking about the "cursor position" actually makes any sense. In this case we
+ // remember a meaningless cursor position. Contrast this with an empty string, which is
+ // valid and should mean the cursor is at the start of the text.
+ // Also, if we expect we don't have a selection but we DO have non-empty selected text,
+ // then the framework lied to us about the cursor position. In this case, we should just
+ // revert to the most basic behavior possible for the next action (backspace in
+ // particular comes to mind), so we remember a meaningless cursor position which should
+ // result in degraded behavior from the next input.
+ // Interestingly, in either case, chances are any action the user takes next will result
+ // in a call to onUpdateSelection, which should set things right.
mExpectedSelStart = mExpectedSelEnd = Constants.NOT_A_CURSOR_POSITION;
} else {
final int textLength = textBeforeCursor.length();
@@ -882,6 +898,15 @@ public final class RichInputConnection {
}
}
+ @Override
+ public boolean performPrivateCommand(final String action, final Bundle data) {
+ mIC = mParent.getCurrentInputConnection();
+ if (mIC == null) {
+ return false;
+ }
+ return mIC.performPrivateCommand(action, data);
+ }
+
public int getExpectedSelectionStart() {
return mExpectedSelStart;
}
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index f85b34b5e..157bd1565 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -128,8 +128,7 @@ public final class WordComposer {
* Number of keystrokes in the composing word.
* @return the number of keystrokes
*/
- // This may be made public if need be, but right now it's not used anywhere
- /* package for tests */ int size() {
+ public int size() {
return mCodePointSize;
}
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 27af1611a..1b1d5e7e5 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -435,8 +435,7 @@ public final class InputLogic {
// removed.
mConnection.removeBackgroundColorFromHighlightedTextIfNecessary();
// We moved the cursor. If we are touching a word, we need to resume suggestion.
- mLatinIME.mHandler.postResumeSuggestions(false /* shouldIncludeResumedWordInSuggestions */,
- true /* shouldDelay */);
+ mLatinIME.mHandler.postResumeSuggestions(true /* shouldDelay */);
// Stop the last recapitalization, if started.
mRecapitalizeStatus.stop();
return true;
@@ -1127,19 +1126,21 @@ public final class InputLogic {
StatsUtils.onBackspaceSelectedText(numCharsDeleted);
} else {
// There is no selection, just delete one character.
- if (Constants.NOT_A_CURSOR_POSITION == mConnection.getExpectedSelectionEnd()) {
- // This should never happen.
- Log.e(TAG, "Backspace when we don't know the selection position");
- }
- if (inputTransaction.mSettingsValues.isBeforeJellyBean() ||
- inputTransaction.mSettingsValues.mInputAttributes.isTypeNull()) {
- // There are two possible reasons to send a key event: either the field has
+ if (inputTransaction.mSettingsValues.isBeforeJellyBean()
+ || inputTransaction.mSettingsValues.mInputAttributes.isTypeNull()
+ || Constants.NOT_A_CURSOR_POSITION
+ == mConnection.getExpectedSelectionEnd()) {
+ // There are three possible reasons to send a key event: either the field has
// type TYPE_NULL, in which case the keyboard should send events, or we are
- // running in backward compatibility mode. Before Jelly bean, the keyboard
- // would simulate a hardware keyboard event on pressing enter or delete. This
- // is bad for many reasons (there are race conditions with commits) but some
- // applications are relying on this behavior so we continue to support it for
- // older apps, so we retain this behavior if the app has target SDK < JellyBean.
+ // running in backward compatibility mode, or we don't know the cursor position.
+ // Before Jelly bean, the keyboard would simulate a hardware keyboard event on
+ // pressing enter or delete. This is bad for many reasons (there are race
+ // conditions with commits) but some applications are relying on this behavior
+ // so we continue to support it for older apps, so we retain this behavior if
+ // the app has target SDK < JellyBean.
+ // As for the case where we don't know the cursor position, it can happen
+ // because of bugs in the framework. But the framework should know, so the next
+ // best thing is to leave it to whatever it thinks is best.
sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL);
int totalDeletedLength = 1;
if (mDeleteCount > Constants.DELETE_ACCELERATE_AT) {
@@ -1184,7 +1185,7 @@ public final class InputLogic {
&& !mConnection.isCursorFollowedByWordCharacter(
inputTransaction.mSettingsValues.mSpacingAndPunctuations)) {
restartSuggestionsOnWordTouchedByCursor(inputTransaction.mSettingsValues,
- true /* shouldIncludeResumedWordInSuggestions */, currentKeyboardScriptId);
+ currentKeyboardScriptId);
}
}
}
@@ -1440,12 +1441,10 @@ public final class InputLogic {
* do nothing.
*
* @param settingsValues the current values of the settings.
- * @param shouldIncludeResumedWordInSuggestions whether to include the word on which we resume
* suggestions in the suggestion list.
*/
// TODO: make this private.
public void restartSuggestionsOnWordTouchedByCursor(final SettingsValues settingsValues,
- final boolean shouldIncludeResumedWordInSuggestions,
// TODO: remove this argument, put it into settingsValues
final int currentKeyboardScriptId) {
// HACK: We may want to special-case some apps that exhibit bad behavior in case of
@@ -1493,13 +1492,6 @@ public final class InputLogic {
if (numberOfCharsInWordBeforeCursor > expectedCursorPosition) return;
final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<>();
final String typedWord = range.mWord.toString();
- if (shouldIncludeResumedWordInSuggestions) {
- suggestions.add(new SuggestedWordInfo(typedWord,
- SuggestedWords.MAX_SUGGESTIONS + 1,
- SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED,
- SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
- SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
- }
if (!isResumableWord(settingsValues, typedWord)) {
mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
return;
@@ -1532,7 +1524,7 @@ public final class InputLogic {
mConnection.maybeMoveTheCursorAroundAndRestoreToWorkaroundABug();
mConnection.setComposingRegion(expectedCursorPosition - numberOfCharsInWordBeforeCursor,
expectedCursorPosition + range.getNumberOfCharsInWordAfterCursor());
- if (suggestions.size() <= (shouldIncludeResumedWordInSuggestions ? 1 : 0)) {
+ if (suggestions.size() <= 0) {
// If there weren't any suggestion spans on this word, suggestions#size() will be 1
// if shouldIncludeResumedWordInSuggestions is true, 0 otherwise. In this case, we
// have no useful suggestions, so we will try to compute some for it instead.
@@ -1542,8 +1534,7 @@ public final class InputLogic {
public void onGetSuggestedWords(
final SuggestedWords suggestedWordsIncludingTypedWord) {
final SuggestedWords suggestedWords;
- if (suggestedWordsIncludingTypedWord.size() > 1
- && !shouldIncludeResumedWordInSuggestions) {
+ if (suggestedWordsIncludingTypedWord.size() > 1) {
// We were able to compute new suggestions for this word.
// Remove the typed word, since we don't want to display it in this
// case. The #getSuggestedWordsExcludingTypedWordForRecorrection()
@@ -1591,6 +1582,10 @@ public final class InputLogic {
final String committedWordString = committedWord.toString();
final int cancelLength = committedWord.length();
final String separatorString = mLastComposedWord.mSeparatorString;
+ // If our separator is a space, we won't actually commit it,
+ // but set the space state to PHANTOM so that a space will be inserted
+ // on the next keypress
+ final boolean usePhantomSpace = separatorString.equals(Constants.STRING_SPACE);
// We want java chars, not codepoints for the following.
final int separatorLength = separatorString.length();
// TODO: should we check our saved separator against the actual contents of the text view?
@@ -1611,7 +1606,8 @@ public final class InputLogic {
if (!TextUtils.isEmpty(committedWord)) {
mDictionaryFacilitator.removeWordFromPersonalizedDicts(committedWordString);
}
- final String stringToCommit = originallyTypedWord + separatorString;
+ final String stringToCommit = originallyTypedWord +
+ (usePhantomSpace ? "" : separatorString);
final SpannableString textToCommit = new SpannableString(stringToCommit);
if (committedWord instanceof SpannableString) {
final SpannableString committedWordWithSuggestionSpans = (SpannableString)committedWord;
@@ -1644,8 +1640,10 @@ public final class InputLogic {
}
}
// Add the suggestion list to the list of suggestions.
- textToCommit.setSpan(new SuggestionSpan(inputTransaction.mSettingsValues.mLocale,
- suggestions.toArray(new String[suggestions.size()]), 0 /* flags */),
+ textToCommit.setSpan(new SuggestionSpan(mLatinIME /* context */,
+ inputTransaction.mSettingsValues.mLocale,
+ suggestions.toArray(new String[suggestions.size()]), 0 /* flags */,
+ null /* notificationTargetClass */),
0 /* start */, lastCharIndex /* end */, 0 /* flags */);
}
@@ -1663,6 +1661,9 @@ public final class InputLogic {
} else {
mConnection.commitText(textToCommit, 1);
}
+ if (usePhantomSpace) {
+ mSpaceState = SpaceState.PHANTOM;
+ }
} else {
// For languages without spaces, we revert the typed string but the cursor is flush
// with the typed word, so we need to resume suggestions right away.
@@ -2186,10 +2187,7 @@ public final class InputLogic {
}
mConnection.tryFixLyingCursorPosition();
if (tryResumeSuggestions) {
- // This is triggered when starting input anew, so we want to include the resumed
- // word in suggestions.
- handler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */,
- true /* shouldDelay */);
+ handler.postResumeSuggestions(true /* shouldDelay */);
}
return true;
}
@@ -2265,6 +2263,47 @@ public final class InputLogic {
mConnection.setComposingText(composingTextToBeSet, newCursorPosition);
}
+ /**
+ * Gets an object allowing private IME commands to be sent to the
+ * underlying editor.
+ * @return An object for sending private commands to the underlying editor.
+ */
+ public PrivateCommandPerformer getPrivateCommandPerformer() {
+ return mConnection;
+ }
+
+ /**
+ * Gets the expected index of the first char of the composing span within the editor's text.
+ * Returns a negative value in case there appears to be no valid composing span.
+ *
+ * @see #getComposingLength()
+ * @see RichInputConnection#hasSelection()
+ * @see RichInputConnection#isCursorPositionKnown()
+ * @see RichInputConnection#getExpectedSelectionStart()
+ * @see RichInputConnection#getExpectedSelectionEnd()
+ * @return The expected index in Java chars of the first char of the composing span.
+ */
+ // TODO: try and see if we can get rid of this method. Ideally the users of this class should
+ // never need to know this.
+ public int getComposingStart() {
+ if (!mConnection.isCursorPositionKnown() || mConnection.hasSelection()) {
+ return -1;
+ }
+ return mConnection.getExpectedSelectionStart() - mWordComposer.size();
+ }
+
+ /**
+ * Gets the expected length in Java chars of the composing span.
+ * May be 0 if there is no valid composing span.
+ * @see #getComposingStart()
+ * @return The expected length of the composing span.
+ */
+ // TODO: try and see if we can get rid of this method. Ideally the users of this class should
+ // never need to know this.
+ public int getComposingLength() {
+ return mWordComposer.size();
+ }
+
//////////////////////////////////////////////////////////////////////////////////////////////
// Following methods are tentatively placed in this class for the integration with
// TextDecorator.
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/PrivateCommandPerformer.java b/java/src/com/android/inputmethod/latin/inputlogic/PrivateCommandPerformer.java
new file mode 100644
index 000000000..42eaa9c82
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/inputlogic/PrivateCommandPerformer.java
@@ -0,0 +1,40 @@
+/*
+ * 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.inputlogic;
+
+import android.os.Bundle;
+
+/**
+ * Provides an interface matching
+ * {@link android.view.inputmethod.InputConnection#performPrivateCommand(String,Bundle)}.
+ */
+public interface PrivateCommandPerformer {
+ /**
+ * API to send private commands from an input method to its connected
+ * editor. This can be used to provide domain-specific features that are
+ * only known between certain input methods and their clients.
+ *
+ * @param action Name of the command to be performed. This must be a scoped
+ * name, i.e. prefixed with a package name you own, so that
+ * different developers will not create conflicting commands.
+ * @param data Any data to include with the command.
+ * @return true if the command was sent (regardless of whether the
+ * associated editor understood it), false if the input connection is no
+ * longer valid.
+ */
+ boolean performPrivateCommand(String action, Bundle data);
+}
diff --git a/java/src/com/android/inputmethod/latin/makedict/NgramProperty.java b/java/src/com/android/inputmethod/latin/makedict/NgramProperty.java
new file mode 100644
index 000000000..99e0e273f
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/makedict/NgramProperty.java
@@ -0,0 +1,26 @@
+package com.android.inputmethod.latin.makedict;
+
+import com.android.inputmethod.latin.NgramContext;
+
+public class NgramProperty {
+ public final WeightedString mTargetWord;
+ public final NgramContext mNgramContext;
+
+ public NgramProperty(final WeightedString targetWord, final NgramContext ngramContext) {
+ mTargetWord = targetWord;
+ mNgramContext = ngramContext;
+ }
+
+ @Override
+ public int hashCode() {
+ return mTargetWord.hashCode() ^ mNgramContext.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (!(o instanceof NgramProperty)) return false;
+ final NgramProperty n = (NgramProperty)o;
+ return mTargetWord.equals(n.mTargetWord) && mNgramContext.equals(n.mNgramContext);
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java
index cd78e2235..46705f9db 100644
--- a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java
+++ b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java
@@ -18,6 +18,8 @@ package com.android.inputmethod.latin.makedict;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.BinaryDictionary;
+import com.android.inputmethod.latin.NgramContext;
+import com.android.inputmethod.latin.NgramContext.WordInfo;
import com.android.inputmethod.latin.utils.CombinedFormatUtils;
import com.android.inputmethod.latin.utils.StringUtils;
@@ -33,16 +35,17 @@ public final class WordProperty implements Comparable<WordProperty> {
public final String mWord;
public final ProbabilityInfo mProbabilityInfo;
public final ArrayList<WeightedString> mShortcutTargets;
- public final ArrayList<WeightedString> mBigrams;
+ public final ArrayList<NgramProperty> mNgrams;
// TODO: Support mIsBeginningOfSentence.
public final boolean mIsBeginningOfSentence;
public final boolean mIsNotAWord;
public final boolean mIsBlacklistEntry;
public final boolean mHasShortcuts;
- public final boolean mHasBigrams;
+ public final boolean mHasNgrams;
private int mHashCode = 0;
+ // TODO: Support n-gram.
@UsedForTesting
public WordProperty(final String word, final ProbabilityInfo probabilityInfo,
final ArrayList<WeightedString> shortcutTargets,
@@ -51,11 +54,17 @@ public final class WordProperty implements Comparable<WordProperty> {
mWord = word;
mProbabilityInfo = probabilityInfo;
mShortcutTargets = shortcutTargets;
- mBigrams = bigrams;
+ mNgrams = new ArrayList<>();
+ final NgramContext ngramContext = new NgramContext(new WordInfo(mWord));
+ if (bigrams != null) {
+ for (final WeightedString bigramTarget : bigrams) {
+ mNgrams.add(new NgramProperty(bigramTarget, ngramContext));
+ }
+ }
mIsBeginningOfSentence = false;
mIsNotAWord = isNotAWord;
mIsBlacklistEntry = isBlacklistEntry;
- mHasBigrams = bigrams != null && !bigrams.isEmpty();
+ mHasNgrams = bigrams != null && !bigrams.isEmpty();
mHasShortcuts = shortcutTargets != null && !shortcutTargets.isEmpty();
}
@@ -78,19 +87,24 @@ public final class WordProperty implements Comparable<WordProperty> {
mWord = StringUtils.getStringFromNullTerminatedCodePointArray(codePoints);
mProbabilityInfo = createProbabilityInfoFromArray(probabilityInfo);
mShortcutTargets = new ArrayList<>();
- mBigrams = new ArrayList<>();
+ mNgrams = new ArrayList<>();
mIsBeginningOfSentence = isBeginningOfSentence;
mIsNotAWord = isNotAWord;
mIsBlacklistEntry = isBlacklisted;
mHasShortcuts = hasShortcuts;
- mHasBigrams = hasBigram;
-
- final int bigramTargetCount = bigramTargets.size();
- for (int i = 0; i < bigramTargetCount; i++) {
- final String bigramTargetString =
+ mHasNgrams = hasBigram;
+
+ final int relatedNgramCount = bigramTargets.size();
+ final WordInfo currentWordInfo =
+ mIsBeginningOfSentence ? WordInfo.BEGINNING_OF_SENTENCE : new WordInfo(mWord);
+ final NgramContext ngramContext = new NgramContext(currentWordInfo);
+ for (int i = 0; i < relatedNgramCount; i++) {
+ final String ngramTargetString =
StringUtils.getStringFromNullTerminatedCodePointArray(bigramTargets.get(i));
- mBigrams.add(new WeightedString(bigramTargetString,
- createProbabilityInfoFromArray(bigramProbabilityInfo.get(i))));
+ final WeightedString ngramTarget = new WeightedString(ngramTargetString,
+ createProbabilityInfoFromArray(bigramProbabilityInfo.get(i)));
+ // TODO: Support n-gram.
+ mNgrams.add(new NgramProperty(ngramTarget, ngramContext));
}
final int shortcutTargetCount = shortcutTargets.size();
@@ -102,6 +116,17 @@ public final class WordProperty implements Comparable<WordProperty> {
}
}
+ // TODO: Remove
+ public ArrayList<WeightedString> getBigrams() {
+ final ArrayList<WeightedString> bigrams = new ArrayList<>();
+ for (final NgramProperty ngram : mNgrams) {
+ if (ngram.mNgramContext.getPrevWordCount() == 1) {
+ bigrams.add(ngram.mTargetWord);
+ }
+ }
+ return bigrams;
+ }
+
public int getProbability() {
return mProbabilityInfo.mProbability;
}
@@ -110,8 +135,8 @@ public final class WordProperty implements Comparable<WordProperty> {
return Arrays.hashCode(new Object[] {
word.mWord,
word.mProbabilityInfo,
- word.mShortcutTargets.hashCode(),
- word.mBigrams.hashCode(),
+ word.mShortcutTargets,
+ word.mNgrams,
word.mIsNotAWord,
word.mIsBlacklistEntry
});
@@ -142,9 +167,9 @@ public final class WordProperty implements Comparable<WordProperty> {
if (!(o instanceof WordProperty)) return false;
WordProperty w = (WordProperty)o;
return mProbabilityInfo.equals(w.mProbabilityInfo) && mWord.equals(w.mWord)
- && mShortcutTargets.equals(w.mShortcutTargets) && mBigrams.equals(w.mBigrams)
+ && mShortcutTargets.equals(w.mShortcutTargets) && mNgrams.equals(w.mNgrams)
&& mIsNotAWord == w.mIsNotAWord && mIsBlacklistEntry == w.mIsBlacklistEntry
- && mHasBigrams == w.mHasBigrams && mHasShortcuts && w.mHasBigrams;
+ && mHasNgrams == w.mHasNgrams && mHasShortcuts && w.mHasNgrams;
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java
index a171fc330..83adb1c55 100644
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ b/java/src/com/android/inputmethod/latin/settings/Settings.java
@@ -32,6 +32,7 @@ import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
import com.android.inputmethod.latin.utils.ResourceUtils;
import com.android.inputmethod.latin.utils.RunInLocale;
+import com.android.inputmethod.latin.utils.StatsUtils;
import com.android.inputmethod.latin.utils.StringUtils;
import java.util.Collections;
@@ -169,6 +170,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
return;
}
loadSettings(mContext, mSettingsValues.mLocale, mSettingsValues.mInputAttributes);
+ StatsUtils.onLoadSettings(mSettingsValues);
} finally {
mSettingsValuesLock.unlock();
}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java b/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java
index 51c4b1ee8..9ddee8629 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java
@@ -145,9 +145,8 @@ public class SentenceLevelAdapter {
int wordEnd = wordIterator.getEndOfWord(originalText, wordStart);
while (wordStart <= end && wordEnd != -1 && wordStart != -1) {
if (wordEnd >= start && wordEnd > wordStart) {
- CharSequence subSequence = originalText.subSequence(wordStart, wordEnd).toString();
- final TextInfo ti = TextInfoCompatUtils.newInstance(subSequence, 0,
- subSequence.length(), cookie, subSequence.hashCode());
+ final TextInfo ti = TextInfoCompatUtils.newInstance(originalText, wordStart,
+ wordEnd, cookie, originalText.subSequence(wordStart, wordEnd).hashCode());
wordItems.add(new SentenceWordItem(ti, wordStart, wordEnd));
}
wordStart = wordIterator.getBeginningOfNextWord(originalText, wordEnd);
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
index 1e8df8986..a651774c6 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
@@ -60,6 +60,9 @@ import com.android.inputmethod.latin.utils.ViewLayoutUtils;
import java.util.ArrayList;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
final class SuggestionStripLayoutHelper {
private static final int DEFAULT_SUGGESTIONS_COUNT_IN_STRIP = 3;
private static final float DEFAULT_CENTER_SUGGESTION_PERCENTILE = 0.40f;
@@ -213,15 +216,14 @@ final class SuggestionStripLayoutHelper {
return word;
}
- final int len = word.length();
final Spannable spannedWord = new SpannableString(word);
final int options = mSuggestionStripOptions;
if ((isAutoCorrection && (options & AUTO_CORRECT_BOLD) != 0)
|| (isTypedWordValid && (options & VALID_TYPED_WORD_BOLD) != 0)) {
- spannedWord.setSpan(BOLD_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ addStyleSpan(spannedWord, BOLD_SPAN);
}
if (isAutoCorrection && (options & AUTO_CORRECT_UNDERLINE) != 0) {
- spannedWord.setSpan(UNDERLINE_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ addStyleSpan(spannedWord, UNDERLINE_SPAN);
}
return spannedWord;
}
@@ -365,17 +367,21 @@ final class SuggestionStripLayoutHelper {
(PunctuationSuggestions)suggestedWords, stripView);
}
+ final boolean shouldShowUiToAcceptTypedWord = Settings.getInstance().getCurrent()
+ .mShouldShowUiToAcceptTypedWord;
+ final int suggestionsCount = suggestedWords.size()
+ - (shouldShowUiToAcceptTypedWord ? /* typed word */ 1 : 0);
final int startIndexOfMoreSuggestions = setupWordViewsAndReturnStartIndexOfMoreSuggestions(
suggestedWords, mSuggestionsCountInStrip);
final TextView centerWordView = mWordViews.get(mCenterPositionInStrip);
final int stripWidth = stripView.getWidth();
final int centerWidth = getSuggestionWidth(mCenterPositionInStrip, stripWidth);
- if (suggestedWords.size() == 1 || getTextScaleX(centerWordView.getText(), centerWidth,
+ if (suggestionsCount == 1 || getTextScaleX(centerWordView.getText(), centerWidth,
centerWordView.getPaint()) < MIN_TEXT_XSCALE) {
// Layout only the most relevant suggested word at the center of the suggestion strip
// by consolidating all slots in the strip.
final int countInStrip = 1;
- mMoreSuggestionsAvailable = (suggestedWords.size() > countInStrip);
+ mMoreSuggestionsAvailable = (suggestionsCount > countInStrip);
layoutWord(mCenterPositionInStrip, stripWidth - mPadding);
stripView.addView(centerWordView);
setLayoutWeight(centerWordView, 1.0f, ViewGroup.LayoutParams.MATCH_PARENT);
@@ -387,7 +393,7 @@ final class SuggestionStripLayoutHelper {
}
final int countInStrip = mSuggestionsCountInStrip;
- mMoreSuggestionsAvailable = (suggestedWords.size() > countInStrip);
+ mMoreSuggestionsAvailable = (suggestionsCount > countInStrip);
int x = 0;
for (int positionInStrip = 0; positionInStrip < countInStrip; positionInStrip++) {
if (positionInStrip != 0) {
@@ -442,10 +448,11 @@ final class SuggestionStripLayoutHelper {
// {@link StyleSpan} in a content description may cause an issue of TTS/TalkBack.
// Use a simple {@link String} to avoid the issue.
wordView.setContentDescription(TextUtils.isEmpty(word) ? null : word.toString());
- final CharSequence text = getEllipsizedText(word, width, wordView.getPaint());
- final float scaleX = getTextScaleX(word, width, wordView.getPaint());
+ final CharSequence text = getEllipsizedTextWithSettingScaleX(
+ word, width, wordView.getPaint());
+ final float scaleX = wordView.getTextScaleX();
wordView.setText(text); // TextView.setText() resets text scale x to 1.0.
- wordView.setTextScaleX(Math.max(scaleX, MIN_TEXT_XSCALE));
+ wordView.setTextScaleX(scaleX);
// A <code>wordView</code> should be disabled when <code>word</code> is empty in order to
// make it unclickable.
// With accessibility touch exploration on, <code>wordView</code> should be enabled even
@@ -558,7 +565,8 @@ final class SuggestionStripLayoutHelper {
final TextView wordView = (TextView)addToDictionaryStrip.findViewById(R.id.word_to_save);
wordView.setTextColor(mColorTypedWord);
final int wordWidth = (int)(width * mCenterSuggestionWeight);
- final CharSequence wordToSave = getEllipsizedText(word, wordWidth, wordView.getPaint());
+ final CharSequence wordToSave = getEllipsizedTextWithSettingScaleX(
+ word, wordWidth, wordView.getPaint());
final float wordScaleX = wordView.getTextScaleX();
wordView.setText(wordToSave);
wordView.setTextScaleX(wordScaleX);
@@ -592,7 +600,7 @@ final class SuggestionStripLayoutHelper {
}
hintView.setTextColor(mColorAutoCorrect);
final float hintScaleX = getTextScaleX(hintText, hintWidth, hintView.getPaint());
- hintView.setText(hintText);
+ hintView.setText(hintText); // TextView.setText() resets text scale x to 1.0.
hintView.setTextScaleX(hintScaleX);
setLayoutWeight(hintView, hintWeight, ViewGroup.LayoutParams.MATCH_PARENT);
}
@@ -604,8 +612,7 @@ final class SuggestionStripLayoutHelper {
final int width = titleView.getWidth() - titleView.getPaddingLeft()
- titleView.getPaddingRight();
titleView.setTextColor(mColorAutoCorrect);
- titleView.setText(importantNoticeTitle);
- titleView.setTextScaleX(1.0f); // Reset textScaleX.
+ titleView.setText(importantNoticeTitle); // TextView.setText() resets text scale x to 1.0.
final float titleScaleX = getTextScaleX(importantNoticeTitle, width, titleView.getPaint());
titleView.setTextScaleX(titleScaleX);
}
@@ -620,18 +627,19 @@ final class SuggestionStripLayoutHelper {
}
}
- private static float getTextScaleX(final CharSequence text, final int maxWidth,
+ private static float getTextScaleX(@Nullable final CharSequence text, final int maxWidth,
final TextPaint paint) {
paint.setTextScaleX(1.0f);
final int width = getTextWidth(text, paint);
if (width <= maxWidth || maxWidth <= 0) {
return 1.0f;
}
- return maxWidth / (float)width;
+ return maxWidth / (float) width;
}
- private static CharSequence getEllipsizedText(final CharSequence text, final int maxWidth,
- final TextPaint paint) {
+ @Nullable
+ private static CharSequence getEllipsizedTextWithSettingScaleX(
+ @Nullable final CharSequence text, final int maxWidth, @Nonnull final TextPaint paint) {
if (text == null) {
return null;
}
@@ -641,62 +649,63 @@ final class SuggestionStripLayoutHelper {
return text;
}
- // Note that TextUtils.ellipsize() use text-x-scale as 1.0 if ellipsize is needed. To
- // get squeezed and ellipsized text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE).
- final float upscaledWidth = maxWidth / MIN_TEXT_XSCALE;
- CharSequence ellipsized = TextUtils.ellipsize(
- text, paint, upscaledWidth, TextUtils.TruncateAt.MIDDLE);
- // For an unknown reason, ellipsized seems to return a text that does indeed fit inside the
- // passed width according to paint.measureText, but not according to paint.getTextWidths.
- // But when rendered, the text seems to actually take up as many pixels as returned by
- // paint.getTextWidths, hence problem.
- // To save this case, we compare the measured size of the new text, and if it's too much,
- // try it again removing the difference. This may still give a text too long by one or
- // two pixels so we take an additional 2 pixels cushion and call it a day.
- // TODO: figure out why getTextWidths and measureText don't agree with each other, and
- // remove the following code.
- final float ellipsizedTextWidth = getTextWidth(ellipsized, paint);
- if (upscaledWidth <= ellipsizedTextWidth) {
- ellipsized = TextUtils.ellipsize(
- text, paint, upscaledWidth - (ellipsizedTextWidth - upscaledWidth) - 2,
- TextUtils.TruncateAt.MIDDLE);
- }
+ // <code>text</code> must be ellipsized with minimum text scale x.
paint.setTextScaleX(MIN_TEXT_XSCALE);
- return ellipsized;
+ final boolean hasBoldStyle = hasStyleSpan(text, BOLD_SPAN);
+ final boolean hasUnderlineStyle = hasStyleSpan(text, UNDERLINE_SPAN);
+ // TextUtils.ellipsize erases any span object existed after ellipsized point.
+ // We have to restore these spans afterward.
+ final CharSequence ellipsizedText = TextUtils.ellipsize(
+ text, paint, maxWidth, TextUtils.TruncateAt.MIDDLE);
+ if (!hasBoldStyle && !hasUnderlineStyle) {
+ return ellipsizedText;
+ }
+ final Spannable spannableText = (ellipsizedText instanceof Spannable)
+ ? (Spannable)ellipsizedText : new SpannableString(ellipsizedText);
+ if (hasBoldStyle) {
+ addStyleSpan(spannableText, BOLD_SPAN);
+ }
+ if (hasUnderlineStyle) {
+ addStyleSpan(spannableText, UNDERLINE_SPAN);
+ }
+ return spannableText;
+ }
+
+ private static boolean hasStyleSpan(@Nullable final CharSequence text,
+ final CharacterStyle style) {
+ if (text instanceof Spanned) {
+ return ((Spanned)text).getSpanStart(style) >= 0;
+ }
+ return false;
}
- private static int getTextWidth(final CharSequence text, final TextPaint paint) {
+ private static void addStyleSpan(@Nonnull final Spannable text, final CharacterStyle style) {
+ text.removeSpan(style);
+ text.setSpan(style, 0, text.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ }
+
+ private static int getTextWidth(@Nullable final CharSequence text, final TextPaint paint) {
if (TextUtils.isEmpty(text)) {
return 0;
}
+ final int length = text.length();
+ final float[] widths = new float[length];
+ final int count;
final Typeface savedTypeface = paint.getTypeface();
- paint.setTypeface(getTextTypeface(text));
- final int len = text.length();
- final float[] widths = new float[len];
- final int count = paint.getTextWidths(text, 0, len, widths);
+ try {
+ paint.setTypeface(getTextTypeface(text));
+ count = paint.getTextWidths(text, 0, length, widths);
+ } finally {
+ paint.setTypeface(savedTypeface);
+ }
int width = 0;
for (int i = 0; i < count; i++) {
width += Math.round(widths[i] + 0.5f);
}
- paint.setTypeface(savedTypeface);
return width;
}
- private static Typeface getTextTypeface(final CharSequence text) {
- if (!(text instanceof SpannableString)) {
- return Typeface.DEFAULT;
- }
-
- final SpannableString ss = (SpannableString)text;
- final StyleSpan[] styles = ss.getSpans(0, text.length(), StyleSpan.class);
- if (styles.length == 0) {
- return Typeface.DEFAULT;
- }
-
- if (styles[0].getStyle() == Typeface.BOLD) {
- return Typeface.DEFAULT_BOLD;
- }
- // TODO: BOLD_ITALIC, ITALIC case?
- return Typeface.DEFAULT;
+ private static Typeface getTextTypeface(@Nullable final CharSequence text) {
+ return hasStyleSpan(text, BOLD_SPAN) ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT;
}
}
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index b421a7eb5..e40fd8800 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -400,6 +400,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
if (mStripVisibilityGroup.isShowingImportantNoticeStrip()) {
return false;
}
+ // Detecting sliding up finger to show {@link MoreSuggestionsView}.
if (!mMoreSuggestionsView.isShowingInParent()) {
mLastX = (int)me.getX();
mLastY = (int)me.getY();
@@ -439,6 +440,11 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
@Override
public boolean onTouchEvent(final MotionEvent me) {
+ if (!mMoreSuggestionsView.isShowingInParent()) {
+ // Ignore any touch event while more suggestions panel hasn't been shown.
+ // Detecting sliding up is done at {@link #onInterceptTouchEvent}.
+ return true;
+ }
// In the sliding input mode. {@link MotionEvent} should be forwarded to
// {@link MoreSuggestionsView}.
final int index = me.getActionIndex();
diff --git a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
index 61292fc36..fb36b7c50 100644
--- a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
@@ -17,6 +17,7 @@
package com.android.inputmethod.latin.utils;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
@@ -40,4 +41,13 @@ public final class CollectionUtils {
}
return list;
}
+
+ /**
+ * Tests whether c contains no elements, true if c is null or c is empty.
+ * @param c Collection to test.
+ * @return Whether c contains no elements.
+ */
+ public static boolean isNullOrEmpty(final Collection c) {
+ return c == null || c.isEmpty();
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java b/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java
index 34f59e8bc..7e8e55990 100644
--- a/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java
@@ -67,7 +67,7 @@ public class CombinedFormatUtils {
builder.append("," + BLACKLISTED_TAG + "=true");
}
builder.append("\n");
- if (wordProperty.mShortcutTargets != null) {
+ if (wordProperty.mHasShortcuts) {
for (final WeightedString shortcutTarget : wordProperty.mShortcutTargets) {
builder.append(" " + SHORTCUT_TAG + "=" + shortcutTarget.mWord);
builder.append(",");
@@ -75,8 +75,9 @@ public class CombinedFormatUtils {
builder.append("\n");
}
}
- if (wordProperty.mBigrams != null) {
- for (final WeightedString bigram : wordProperty.mBigrams) {
+ if (wordProperty.mHasNgrams) {
+ // TODO: Support ngram.
+ for (final WeightedString bigram : wordProperty.getBigrams()) {
builder.append(" " + BIGRAM_TAG + "=" + bigram.mWord);
builder.append(",");
builder.append(formatProbabilityInfo(bigram.mProbabilityInfo));