aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--java/src/com/android/inputmethod/keyboard/MainKeyboardView.java6
-rw-r--r--java/src/com/android/inputmethod/keyboard/PointerTracker.java13
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java6
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java12
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java2
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java174
-rw-r--r--java/src/com/android/inputmethod/latin/UserBinaryDictionary.java3
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java22
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java7
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java8
-rw-r--r--java/src/com/android/inputmethod/latin/utils/JsonUtils.java103
-rw-r--r--java/src/com/android/inputmethod/latin/utils/StringUtils.java74
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h4
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.cpp6
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.cpp4
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.h2
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/single_dict_content.h6
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/sparse_table_dict_content.cpp12
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.cpp6
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h2
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp25
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp8
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h2
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.cpp16
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.h3
-rw-r--r--tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java5
-rw-r--r--tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java5
-rw-r--r--tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java10
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java (renamed from tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java)8
-rw-r--r--tools/make-keyboard-text/res/values-es/donottranslate-more-keys.xml2
-rw-r--r--tools/make-keyboard-text/res/values/donottranslate-more-keys.xml3
31 files changed, 310 insertions, 249 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index e8a89712c..1400e05c8 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -1008,6 +1008,12 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
final int index = me.getActionIndex();
final int id = me.getPointerId(index);
final PointerTracker tracker = PointerTracker.getPointerTracker(id);
+ // When a more keys panel is showing, we should ignore other fingers' single touch events
+ // other than the finger that is showing the more keys panel.
+ if (isShowingMoreKeysPanel() && !tracker.isShowingMoreKeysPanel()
+ && PointerTracker.getActivePointerTrackerCount() == 1) {
+ return true;
+ }
tracker.processMotionEvent(me, mKeyDetector);
return true;
}
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index ae6aee4a1..f9e78bfd9 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -715,7 +715,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
return newKey;
}
- private static int getActivePointerTrackerCount() {
+ /* package */ static int getActivePointerTrackerCount() {
return sPointerTrackerQueue.size();
}
@@ -827,12 +827,19 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
final int action = me.getActionMasked();
final long eventTime = me.getEventTime();
if (action == MotionEvent.ACTION_MOVE) {
+ // When this pointer is the only active pointer and is showing a more keys panel,
+ // we should ignore other pointers' motion event.
+ final boolean shouldIgnoreOtherPointers =
+ isShowingMoreKeysPanel() && getActivePointerTrackerCount() == 1;
final int pointerCount = me.getPointerCount();
for (int index = 0; index < pointerCount; index++) {
final int id = me.getPointerId(index);
- final PointerTracker tracker = getPointerTracker(id);
+ if (shouldIgnoreOtherPointers && id != mPointerId) {
+ continue;
+ }
final int x = (int)me.getX(index);
final int y = (int)me.getY(index);
+ final PointerTracker tracker = getPointerTracker(id);
tracker.onMoveEvent(x, y, eventTime, me);
}
return;
@@ -903,7 +910,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
}
}
- private boolean isShowingMoreKeysPanel() {
+ /* package */ boolean isShowingMoreKeysPanel() {
return (mMoreKeysPanel != null);
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
index f0feb2513..e2fd39017 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
@@ -25,7 +25,7 @@ import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.latin.settings.Settings;
import com.android.inputmethod.latin.utils.CollectionUtils;
-import com.android.inputmethod.latin.utils.StringUtils;
+import com.android.inputmethod.latin.utils.JsonUtils;
import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -139,7 +139,7 @@ public class DynamicGridKeyboard extends Keyboard {
keys.add(key.getCode());
}
}
- final String jsonStr = StringUtils.listToJsonStr(keys);
+ final String jsonStr = JsonUtils.listToJsonStr(keys);
Settings.writeEmojiRecentKeys(mPrefs, jsonStr);
}
@@ -167,7 +167,7 @@ public class DynamicGridKeyboard extends Keyboard {
public void loadRecentKeys(final Collection<DynamicGridKeyboard> keyboards) {
final String str = Settings.readEmojiRecentKeys(mPrefs);
- final List<Object> keys = StringUtils.jsonStrToList(str);
+ final List<Object> keys = JsonUtils.jsonStrToList(str);
for (final Object o : keys) {
final Key key;
if (o instanceof Integer) {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
index f1b5bc116..1b722c5ce 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
@@ -384,7 +384,8 @@ public final class KeyboardTextsSet {
/* 122 */ "w",
/* 123 */ "y",
/* 124 */ "x",
- /* 125 */ EMPTY,
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ /* 125 */ "\u00F1",
/* 126 */ "!fixedColumnOrder!2,!hasLabels!,!text/label_time_am,!text/label_time_pm",
/* 127 */ "!icon/settings_key|!code/key_settings",
/* 128 */ "!icon/shortcut_key|!code/key_shortcut",
@@ -1286,15 +1287,6 @@ public final class KeyboardTextsSet {
// U+00A1: "¡" INVERTED EXCLAMATION MARK
// U+00BF: "¿" INVERTED QUESTION MARK
/* 59 */ "!fixedColumnOrder!9,\u00A1,;,/,(,),#,!,\\,,?,\u00BF,&,\\%,+,\",-,:,',@",
- /* 60~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null,
- /* ~124 */
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- /* 125 */ "\u00F1",
};
/* Language et_EE: Estonian (Estonia) */
diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
index b70362f90..b6cfcd064 100644
--- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
@@ -72,7 +72,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
private final boolean mUseFirstLastBigrams;
public ContactsBinaryDictionary(final Context context, final Locale locale) {
- super(context, getFilenameWithLocale(NAME, locale), locale,
+ super(context, getDictNameWithLocale(NAME, locale), locale,
Dictionary.TYPE_CONTACTS, false /* isUpdatable */);
mLocale = locale;
mUseFirstLastBigrams = useFirstLastBigramsForLocale(locale);
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index 5d1c39666..b52045e3c 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -69,14 +69,14 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
/**
* A static map of update controllers, each of which records the time of accesses to a single
* binary dictionary file and tracks whether the file is regenerating. The key for this map is
- * the filename and the value is the shared dictionary time recorder associated with that
- * filename.
+ * the dictionary name and the value is the shared dictionary time recorder associated with
+ * that dictionary name.
*/
private static final ConcurrentHashMap<String, DictionaryUpdateController>
- sFilenameDictionaryUpdateControllerMap = CollectionUtils.newConcurrentHashMap();
+ sDictNameDictionaryUpdateControllerMap = CollectionUtils.newConcurrentHashMap();
private static final ConcurrentHashMap<String, PrioritizedSerialExecutor>
- sFilenameExecutorMap = CollectionUtils.newConcurrentHashMap();
+ sDictNameExecutorMap = CollectionUtils.newConcurrentHashMap();
/** The application context. */
protected final Context mContext;
@@ -92,11 +92,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
protected AbstractDictionaryWriter mDictionaryWriter;
/**
- * The name of this dictionary, used as the filename for storing the binary dictionary. Multiple
- * dictionary instances with the same filename is supported, with access controlled by
- * DictionaryTimeRecorder.
+ * The name of this dictionary, used as a part of the filename for storing the binary
+ * dictionary. Multiple dictionary instances with the same name is supported, with access
+ * controlled by DictionaryUpdateController.
*/
- private final String mFilename;
+ private final String mDictName;
/** Dictionary locale */
private final Locale mLocale;
@@ -106,7 +106,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
// TODO: remove, once dynamic operations is serialized
/** Controls updating the shared binary dictionary file across multiple instances. */
- private final DictionaryUpdateController mFilenameDictionaryUpdateController;
+ private final DictionaryUpdateController mDictNameDictionaryUpdateController;
// TODO: remove, once dynamic operations is serialized
/** Controls updating the local binary dictionary for this instance. */
@@ -144,34 +144,46 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
return mBinaryDictionary.isValidDictionary();
}
- protected String getFileNameExtensionToOpenDict() {
- return "";
+ protected String getFileNameToCreateDict(final String dictName) {
+ return dictName + DICT_FILE_EXTENSION;
+ }
+
+ protected String getFileNameToOpenDict(final String dictName) {
+ return getFileNameToCreateDict(dictName);
+ }
+
+ private File getFileToCreateDict() {
+ return new File(mContext.getFilesDir(), getFileNameToCreateDict(mDictName));
+ }
+
+ private File getFileToOpenDict() {
+ return new File(mContext.getFilesDir(), getFileNameToOpenDict(mDictName));
}
/**
- * Gets the dictionary update controller for the given filename.
+ * Gets the dictionary update controller for the given dictionary name.
*/
private static DictionaryUpdateController getDictionaryUpdateController(
- String filename) {
- DictionaryUpdateController recorder = sFilenameDictionaryUpdateControllerMap.get(filename);
+ final String dictName) {
+ DictionaryUpdateController recorder = sDictNameDictionaryUpdateControllerMap.get(dictName);
if (recorder == null) {
- synchronized(sFilenameDictionaryUpdateControllerMap) {
+ synchronized(sDictNameDictionaryUpdateControllerMap) {
recorder = new DictionaryUpdateController();
- sFilenameDictionaryUpdateControllerMap.put(filename, recorder);
+ sDictNameDictionaryUpdateControllerMap.put(dictName, recorder);
}
}
return recorder;
}
/**
- * Gets the executor for the given filename.
+ * Gets the executor for the given dictionary name.
*/
- private static PrioritizedSerialExecutor getExecutor(final String filename) {
- PrioritizedSerialExecutor executor = sFilenameExecutorMap.get(filename);
+ private static PrioritizedSerialExecutor getExecutor(final String dictName) {
+ PrioritizedSerialExecutor executor = sDictNameExecutorMap.get(dictName);
if (executor == null) {
- synchronized(sFilenameExecutorMap) {
+ synchronized(sDictNameExecutorMap) {
executor = new PrioritizedSerialExecutor();
- sFilenameExecutorMap.put(filename, executor);
+ sDictNameExecutorMap.put(dictName, executor);
}
}
return executor;
@@ -190,28 +202,28 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
* Creates a new expandable binary dictionary.
*
* @param context The application context of the parent.
- * @param filename The filename for this binary dictionary. Multiple dictionaries with the same
- * filename is supported.
+ * @param dictName The name of the dictionary. Multiple instances with the same
+ * name is supported.
* @param locale the dictionary locale.
* @param dictType the dictionary type, as a human-readable string
* @param isUpdatable whether to support dynamically updating the dictionary. Please note that
* dynamic dictionary has negative effects on memory space and computation time.
*/
- public ExpandableBinaryDictionary(final Context context, final String filename,
+ public ExpandableBinaryDictionary(final Context context, final String dictName,
final Locale locale, final String dictType, final boolean isUpdatable) {
super(dictType);
- mFilename = filename;
+ mDictName = dictName;
mContext = context;
mLocale = locale;
mIsUpdatable = isUpdatable;
mBinaryDictionary = null;
- mFilenameDictionaryUpdateController = getDictionaryUpdateController(filename);
+ mDictNameDictionaryUpdateController = getDictionaryUpdateController(dictName);
// Currently, only dynamic personalization dictionary is updatable.
mDictionaryWriter = getDictionaryWriter(context, isUpdatable);
}
- protected static String getFilenameWithLocale(final String name, final Locale locale) {
- return name + "." + locale.toString() + DICT_FILE_EXTENSION;
+ protected static String getDictNameWithLocale(final String name, final Locale locale) {
+ return name + "." + locale.toString();
}
/**
@@ -219,7 +231,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
*/
@Override
public void close() {
- getExecutor(mFilename).execute(new Runnable() {
+ getExecutor(mDictName).execute(new Runnable() {
@Override
public void run() {
if (mBinaryDictionary!= null) {
@@ -232,7 +244,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
protected void closeBinaryDictionary() {
// Ensure that no other threads are accessing the local binary dictionary.
- getExecutor(mFilename).execute(new Runnable() {
+ getExecutor(mDictName).execute(new Runnable() {
@Override
public void run() {
if (mBinaryDictionary != null) {
@@ -245,7 +257,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
protected Map<String, String> getHeaderAttributeMap() {
HashMap<String, String> attributeMap = new HashMap<String, String>();
- attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_ATTRIBUTE, mFilename);
+ attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_ATTRIBUTE, mDictName);
attributeMap.put(FormatSpec.FileHeader.DICTIONARY_LOCALE_ATTRIBUTE, mLocale.toString());
attributeMap.put(FormatSpec.FileHeader.DICTIONARY_VERSION_ATTRIBUTE,
String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
@@ -253,19 +265,18 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
protected void clear() {
- getExecutor(mFilename).execute(new Runnable() {
+ getExecutor(mDictName).execute(new Runnable() {
@Override
public void run() {
if (mDictionaryWriter == null) {
mBinaryDictionary.close();
- final File file = new File(mContext.getFilesDir(), mFilename);
+ final File file = getFileToCreateDict();
file.delete();
BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(),
DICTIONARY_FORMAT_VERSION, getHeaderAttributeMap());
// We have 'fileToOpen' in addition to 'file' for the v4 dictionary format
// where 'file' is a directory, and 'fileToOpen' is a normal file.
- final File fileToOpen = new File(mContext.getFilesDir(), mFilename
- + getFileNameExtensionToOpenDict());
+ final File fileToOpen = getFileToOpenDict();
// TODO: Make BinaryDictionary's constructor be able to accept filename
// without extension.
mBinaryDictionary = new BinaryDictionary(
@@ -305,7 +316,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
* Check whether GC is needed and run GC if required.
*/
protected void runGCIfRequired(final boolean mindsBlockByGC) {
- getExecutor(mFilename).execute(new Runnable() {
+ getExecutor(mDictName).execute(new Runnable() {
@Override
public void run() {
runGCIfRequiredInternalLocked(mindsBlockByGC);
@@ -318,13 +329,13 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
if (mBinaryDictionary.needsToRunGC(mindsBlockByGC)) {
if (setProcessingLargeTaskIfNot()) {
// Run GC after currently existing time sensitive operations.
- getExecutor(mFilename).executePrioritized(new Runnable() {
+ getExecutor(mDictName).executePrioritized(new Runnable() {
@Override
public void run() {
try {
mBinaryDictionary.flushWithGC();
} finally {
- mFilenameDictionaryUpdateController.mProcessingLargeTask.set(false);
+ mDictNameDictionaryUpdateController.mProcessingLargeTask.set(false);
}
}
});
@@ -339,10 +350,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord,
final boolean isBlacklisted, final int timestamp) {
if (!mIsUpdatable) {
- Log.w(TAG, "addWordDynamically is called for non-updatable dictionary: " + mFilename);
+ Log.w(TAG, "addWordDynamically is called for non-updatable dictionary: " + mDictName);
return;
}
- getExecutor(mFilename).execute(new Runnable() {
+ getExecutor(mDictName).execute(new Runnable() {
@Override
public void run() {
runGCIfRequiredInternalLocked(true /* mindsBlockByGC */);
@@ -359,10 +370,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
final int frequency, final int timestamp) {
if (!mIsUpdatable) {
Log.w(TAG, "addBigramDynamically is called for non-updatable dictionary: "
- + mFilename);
+ + mDictName);
return;
}
- getExecutor(mFilename).execute(new Runnable() {
+ getExecutor(mDictName).execute(new Runnable() {
@Override
public void run() {
runGCIfRequiredInternalLocked(true /* mindsBlockByGC */);
@@ -377,10 +388,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
protected void removeBigramDynamically(final String word0, final String word1) {
if (!mIsUpdatable) {
Log.w(TAG, "removeBigramDynamically is called for non-updatable dictionary: "
- + mFilename);
+ + mDictName);
return;
}
- getExecutor(mFilename).execute(new Runnable() {
+ getExecutor(mDictName).execute(new Runnable() {
@Override
public void run() {
runGCIfRequiredInternalLocked(true /* mindsBlockByGC */);
@@ -401,10 +412,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
final AddMultipleDictionaryEntriesCallback callback) {
if (!mIsUpdatable) {
Log.w(TAG, "addMultipleDictionaryEntriesDynamically is called for non-updatable " +
- "dictionary: " + mFilename);
+ "dictionary: " + mDictName);
return;
}
- getExecutor(mFilename).execute(new Runnable() {
+ getExecutor(mDictName).execute(new Runnable() {
@Override
public void run() {
final boolean locked = setProcessingLargeTaskIfNot();
@@ -417,7 +428,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
callback.onFinished();
}
if (locked) {
- mFilenameDictionaryUpdateController.mProcessingLargeTask.set(false);
+ mDictNameDictionaryUpdateController.mProcessingLargeTask.set(false);
}
}
}
@@ -435,7 +446,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
final AsyncResultHolder<ArrayList<SuggestedWordInfo>> holder =
new AsyncResultHolder<ArrayList<SuggestedWordInfo>>();
- getExecutor(mFilename).executePrioritized(new Runnable() {
+ getExecutor(mDictName).executePrioritized(new Runnable() {
@Override
public void run() {
if (mBinaryDictionary == null) {
@@ -471,7 +482,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
return false;
}
final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>();
- getExecutor(mFilename).executePrioritized(new Runnable() {
+ getExecutor(mDictName).executePrioritized(new Runnable() {
@Override
public void run() {
holder.set(isValidWordLocked(word));
@@ -505,23 +516,22 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
*/
private void loadBinaryDictionary() {
if (DEBUG) {
- Log.d(TAG, "Loading binary dictionary: " + mFilename + " request="
- + mFilenameDictionaryUpdateController.mLastUpdateRequestTime + " update="
- + mFilenameDictionaryUpdateController.mLastUpdateTime);
+ Log.d(TAG, "Loading binary dictionary: " + mDictName + " request="
+ + mDictNameDictionaryUpdateController.mLastUpdateRequestTime + " update="
+ + mDictNameDictionaryUpdateController.mLastUpdateTime);
}
if (DBG_STRESS_TEST) {
// Test if this class does not cause problems when it takes long time to load binary
// dictionary.
try {
- Log.w(TAG, "Start stress in loading: " + mFilename);
+ Log.w(TAG, "Start stress in loading: " + mDictName);
Thread.sleep(15000);
Log.w(TAG, "End stress in loading");
} catch (InterruptedException e) {
}
}
- final File file = new File(mContext.getFilesDir(), mFilename
- + getFileNameExtensionToOpenDict());
+ final File file = getFileToOpenDict();
final String filename = file.getAbsolutePath();
final long length = file.length();
@@ -533,7 +543,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
// swapping in the new one.
// TODO: Ensure multi-thread assignment of mBinaryDictionary.
final BinaryDictionary oldBinaryDictionary = mBinaryDictionary;
- getExecutor(mFilename).executePrioritized(new Runnable() {
+ getExecutor(mDictName).executePrioritized(new Runnable() {
@Override
public void run() {
mBinaryDictionary = newBinaryDictionary;
@@ -555,20 +565,20 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
*/
private void writeBinaryDictionary() {
if (DEBUG) {
- Log.d(TAG, "Generating binary dictionary: " + mFilename + " request="
- + mFilenameDictionaryUpdateController.mLastUpdateRequestTime + " update="
- + mFilenameDictionaryUpdateController.mLastUpdateTime);
+ Log.d(TAG, "Generating binary dictionary: " + mDictName + " request="
+ + mDictNameDictionaryUpdateController.mLastUpdateRequestTime + " update="
+ + mDictNameDictionaryUpdateController.mLastUpdateTime);
}
if (needsToReloadBeforeWriting()) {
mDictionaryWriter.clear();
loadDictionaryAsync();
- mDictionaryWriter.write(mFilename, getHeaderAttributeMap());
+ mDictionaryWriter.write(getFileNameToCreateDict(mDictName), getHeaderAttributeMap());
} else {
if (mBinaryDictionary == null || !isValidDictionary()
// TODO: remove the check below
|| !matchesExpectedBinaryDictFormatVersionForThisType(
mBinaryDictionary.getFormatVersion())) {
- final File file = new File(mContext.getFilesDir(), mFilename);
+ final File file = getFileToCreateDict();
if (!FileUtils.deleteRecursively(file)) {
Log.e(TAG, "Can't remove a file: " + file.getName());
}
@@ -594,10 +604,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
protected void setRequiresReload(final boolean requiresRebuild) {
final long time = SystemClock.uptimeMillis();
mPerInstanceDictionaryUpdateController.mLastUpdateRequestTime = time;
- mFilenameDictionaryUpdateController.mLastUpdateRequestTime = time;
+ mDictNameDictionaryUpdateController.mLastUpdateRequestTime = time;
if (DEBUG) {
- Log.d(TAG, "Reload request: " + mFilename + ": request=" + time + " update="
- + mFilenameDictionaryUpdateController.mLastUpdateTime);
+ Log.d(TAG, "Reload request: " + mDictName + ": request=" + time + " update="
+ + mDictNameDictionaryUpdateController.mLastUpdateTime);
}
}
@@ -619,13 +629,13 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
private boolean processingLargeTask() {
- return mFilenameDictionaryUpdateController.mProcessingLargeTask.get();
+ return mDictNameDictionaryUpdateController.mProcessingLargeTask.get();
}
// Returns whether the dictionary is being used for a large task. If true, we should not use
// this dictionary for latency sensitive operations.
private boolean setProcessingLargeTaskIfNot() {
- return mFilenameDictionaryUpdateController.mProcessingLargeTask.compareAndSet(
+ return mDictNameDictionaryUpdateController.mProcessingLargeTask.compareAndSet(
false /* expect */ , true /* update */);
}
@@ -636,13 +646,13 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
private final void reloadDictionary() {
// Ensure that only one thread attempts to read or write to the shared binary dictionary
// file at the same time.
- getExecutor(mFilename).execute(new Runnable() {
+ getExecutor(mDictName).execute(new Runnable() {
@Override
public void run() {
try {
final long time = SystemClock.uptimeMillis();
final boolean dictionaryFileExists = dictionaryFileExists();
- if (mFilenameDictionaryUpdateController.isOutOfDate()
+ if (mDictNameDictionaryUpdateController.isOutOfDate()
|| !dictionaryFileExists) {
// If the shared dictionary file does not exist or is out of date, the
// first instance that acquires the lock will generate a new one.
@@ -651,18 +661,18 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
// rebuild the binary dictionary. Empty dictionaries are supported (in
// the case where loadDictionaryAsync() adds nothing) in order to
// provide a uniform framework.
- mFilenameDictionaryUpdateController.mLastUpdateTime = time;
+ mDictNameDictionaryUpdateController.mLastUpdateTime = time;
writeBinaryDictionary();
loadBinaryDictionary();
} else {
// If not, the reload request was unnecessary so revert
// LastUpdateRequestTime to LastUpdateTime.
- mFilenameDictionaryUpdateController.mLastUpdateRequestTime =
- mFilenameDictionaryUpdateController.mLastUpdateTime;
+ mDictNameDictionaryUpdateController.mLastUpdateRequestTime =
+ mDictNameDictionaryUpdateController.mLastUpdateTime;
}
} else if (mBinaryDictionary == null ||
mPerInstanceDictionaryUpdateController.mLastUpdateTime
- < mFilenameDictionaryUpdateController.mLastUpdateTime) {
+ < mDictNameDictionaryUpdateController.mLastUpdateTime) {
// Otherwise, if the local dictionary is older than the shared dictionary,
// load the shared dictionary.
loadBinaryDictionary();
@@ -670,7 +680,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
// If we just loaded the binary dictionary, then mBinaryDictionary is not
// up-to-date yet so it's useless to test it right away. Schedule the check
// for right after it's loaded instead.
- getExecutor(mFilename).executePrioritized(new Runnable() {
+ getExecutor(mDictName).executePrioritized(new Runnable() {
@Override
public void run() {
if (mBinaryDictionary != null && !(isValidDictionary()
@@ -680,7 +690,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
// Binary dictionary or its format version is not valid. Regenerate
// the dictionary file. writeBinaryDictionary will remove the
// existing files if appropriate.
- mFilenameDictionaryUpdateController.mLastUpdateTime = time;
+ mDictNameDictionaryUpdateController.mLastUpdateTime = time;
writeBinaryDictionary();
loadBinaryDictionary();
}
@@ -688,7 +698,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
});
} finally {
- mFilenameDictionaryUpdateController.mProcessingLargeTask.set(false);
+ mDictNameDictionaryUpdateController.mProcessingLargeTask.set(false);
}
}
});
@@ -696,7 +706,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
// TODO: cache the file's existence so that we avoid doing a disk access each time.
private boolean dictionaryFileExists() {
- final File file = new File(mContext.getFilesDir(), mFilename);
+ final File file = getFileToOpenDict();
return file.exists();
}
@@ -711,7 +721,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
};
final Runnable oldTask = mUnfinishedFlushingTask.getAndSet(newTask);
- getExecutor(mFilename).replaceAndExecute(oldTask, newTask);
+ getExecutor(mDictName).replaceAndExecute(oldTask, newTask);
}
/**
@@ -732,7 +742,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
@UsedForTesting
public boolean isInDictionaryForTests(final String word) {
final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>();
- getExecutor(mFilename).executePrioritized(new Runnable() {
+ getExecutor(mDictName).executePrioritized(new Runnable() {
@Override
public void run() {
if (mDictType == Dictionary.TYPE_USER_HISTORY) {
@@ -745,24 +755,24 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
@UsedForTesting
public void shutdownExecutorForTests() {
- getExecutor(mFilename).shutdown();
+ getExecutor(mDictName).shutdown();
}
@UsedForTesting
public boolean isTerminatedForTests() {
- return getExecutor(mFilename).isTerminated();
+ return getExecutor(mDictName).isTerminated();
}
@UsedForTesting
protected void runAfterGcForDebug(final Runnable r) {
- getExecutor(mFilename).executePrioritized(new Runnable() {
+ getExecutor(mDictName).executePrioritized(new Runnable() {
@Override
public void run() {
try {
mBinaryDictionary.flushWithGC();
r.run();
} finally {
- mFilenameDictionaryUpdateController.mProcessingLargeTask.set(false);
+ mDictNameDictionaryUpdateController.mProcessingLargeTask.set(false);
}
}
});
diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
index cc7687b62..1dc3c54bb 100644
--- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
@@ -18,7 +18,6 @@ package com.android.inputmethod.latin;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
-import android.content.ContentUris;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
@@ -81,7 +80,7 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary {
public UserBinaryDictionary(final Context context, final Locale locale,
final boolean alsoUseMoreRestrictiveLocales) {
- super(context, getFilenameWithLocale(NAME, locale), locale, Dictionary.TYPE_USER,
+ super(context, getDictNameWithLocale(NAME, locale), locale, Dictionary.TYPE_USER,
false /* isUpdatable */);
if (null == locale) throw new NullPointerException(); // Catch the error earlier
final String localeStr = locale.toString();
diff --git a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
index 386e1232f..8321df94b 100644
--- a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
+++ b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
@@ -58,13 +58,13 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB
/** Locale for which this user history dictionary is storing words */
private final Locale mLocale;
- private final String mFileName;
+ private final String mDictName;
/* package */ DecayingExpandableBinaryDictionaryBase(final Context context,
- final Locale locale, final String dictionaryType, final String fileName) {
- super(context, fileName, locale, dictionaryType, true);
+ final Locale locale, final String dictionaryType, final String dictName) {
+ super(context, dictName, locale, dictionaryType, true);
mLocale = locale;
- mFileName = fileName;
+ mDictName = dictName;
if (mLocale != null && mLocale.toString().length() > 1) {
reloadDictionaryIfRequired();
}
@@ -88,7 +88,7 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB
FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE);
attributeMap.put(FormatSpec.FileHeader.HAS_HISTORICAL_INFO_ATTRIBUTE,
FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE);
- attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_ATTRIBUTE, mFileName);
+ attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_ATTRIBUTE, mDictName);
attributeMap.put(FormatSpec.FileHeader.DICTIONARY_LOCALE_ATTRIBUTE, mLocale.toString());
attributeMap.put(FormatSpec.FileHeader.DICTIONARY_VERSION_ATTRIBUTE,
String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
@@ -113,9 +113,13 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB
}
@Override
- protected String getFileNameExtensionToOpenDict() {
- // TODO: pass the directory name instead
- return "/" + FormatSpec.HEADER_FILE_EXTENSION;
+ protected String getFileNameToCreateDict(final String dictName) {
+ return dictName;
+ }
+
+ @Override
+ protected String getFileNameToOpenDict(final String dictName) {
+ return dictName + "/" + dictName + FormatSpec.HEADER_FILE_EXTENSION;
}
public void addMultipleDictionaryEntriesToDictionary(
@@ -194,7 +198,7 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB
};
// Load the dictionary from binary file
- final File dictFile = new File(mContext.getFilesDir(), mFileName);
+ final File dictFile = new File(mContext.getFilesDir(), mDictName);
final DictDecoder dictDecoder = FormatSpec.getDictDecoder(dictFile,
DictDecoder.USE_BYTEARRAY);
if (dictDecoder == null) {
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
index 413a951ad..b1ec76f28 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
@@ -17,7 +17,6 @@
package com.android.inputmethod.latin.personalization;
import com.android.inputmethod.latin.Dictionary;
-import com.android.inputmethod.latin.ExpandableBinaryDictionary;
import com.android.inputmethod.latin.utils.CollectionUtils;
import java.util.ArrayList;
@@ -33,11 +32,7 @@ public class PersonalizationDictionary extends DecayingExpandableBinaryDictionar
/* package */ PersonalizationDictionary(final Context context, final Locale locale) {
super(context, locale, Dictionary.TYPE_PERSONALIZATION,
- getDictionaryFileName(locale.toString()));
- }
-
- private static String getDictionaryFileName(final String locale) {
- return NAME + "." + locale + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
+ getDictNameWithLocale(NAME, locale));
}
public void registerUpdateSession(PersonalizationDictionaryUpdateSession session) {
diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
index 975224f7c..3f03de0d4 100644
--- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
@@ -17,7 +17,6 @@
package com.android.inputmethod.latin.personalization;
import com.android.inputmethod.latin.Dictionary;
-import com.android.inputmethod.latin.ExpandableBinaryDictionary;
import java.util.Locale;
@@ -31,12 +30,7 @@ public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBas
/* package for tests */ static final String NAME =
UserHistoryDictionary.class.getSimpleName();
/* package */ UserHistoryDictionary(final Context context, final Locale locale) {
- super(context, locale, Dictionary.TYPE_USER_HISTORY,
- getDictionaryFileName(locale.toString()));
- }
-
- private static String getDictionaryFileName(final String locale) {
- return NAME + "." + locale + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
+ super(context, locale, Dictionary.TYPE_USER_HISTORY, getDictNameWithLocale(NAME, locale));
}
public void cancelAddingUserHistory(final String word0, final String word1) {
diff --git a/java/src/com/android/inputmethod/latin/utils/JsonUtils.java b/java/src/com/android/inputmethod/latin/utils/JsonUtils.java
new file mode 100644
index 000000000..764ef72ce
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/utils/JsonUtils.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.utils;
+
+import android.util.JsonReader;
+import android.util.JsonWriter;
+import android.util.Log;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public final class JsonUtils {
+ private static final String TAG = JsonUtils.class.getSimpleName();
+
+ private static final String INTEGER_CLASS_NAME = Integer.class.getSimpleName();
+ private static final String STRING_CLASS_NAME = String.class.getSimpleName();
+
+ private static final String EMPTY_STRING = "";
+
+ public static List<Object> jsonStrToList(final String s) {
+ final ArrayList<Object> list = CollectionUtils.newArrayList();
+ final JsonReader reader = new JsonReader(new StringReader(s));
+ try {
+ reader.beginArray();
+ while (reader.hasNext()) {
+ reader.beginObject();
+ while (reader.hasNext()) {
+ final String name = reader.nextName();
+ if (name.equals(INTEGER_CLASS_NAME)) {
+ list.add(reader.nextInt());
+ } else if (name.equals(STRING_CLASS_NAME)) {
+ list.add(reader.nextString());
+ } else {
+ Log.w(TAG, "Invalid name: " + name);
+ reader.skipValue();
+ }
+ }
+ reader.endObject();
+ }
+ reader.endArray();
+ return list;
+ } catch (final IOException e) {
+ } finally {
+ close(reader);
+ }
+ return Collections.<Object>emptyList();
+ }
+
+ public static String listToJsonStr(final List<Object> list) {
+ if (list == null || list.isEmpty()) {
+ return EMPTY_STRING;
+ }
+ final StringWriter sw = new StringWriter();
+ final JsonWriter writer = new JsonWriter(sw);
+ try {
+ writer.beginArray();
+ for (final Object o : list) {
+ writer.beginObject();
+ if (o instanceof Integer) {
+ writer.name(INTEGER_CLASS_NAME).value((Integer)o);
+ } else if (o instanceof String) {
+ writer.name(STRING_CLASS_NAME).value((String)o);
+ }
+ writer.endObject();
+ }
+ writer.endArray();
+ return sw.toString();
+ } catch (final IOException e) {
+ } finally {
+ close(writer);
+ }
+ return EMPTY_STRING;
+ }
+
+ private static void close(final Closeable closeable) {
+ try {
+ if (closeable != null) {
+ closeable.close();
+ }
+ } catch (final IOException e) {
+ // Ignore
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
index 928000ec9..df420417d 100644
--- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
@@ -17,20 +17,14 @@
package com.android.inputmethod.latin.utils;
import android.text.TextUtils;
-import android.util.JsonReader;
-import android.util.JsonWriter;
import android.util.Log;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.settings.SettingsValues;
-import java.io.Closeable;
import java.io.IOException;
-import java.io.StringReader;
-import java.io.StringWriter;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Locale;
@@ -426,72 +420,4 @@ public final class StringUtils {
}
return bytes;
}
-
- private static final String INTEGER_CLASS_NAME = Integer.class.getSimpleName();
- private static final String STRING_CLASS_NAME = String.class.getSimpleName();
-
- public static List<Object> jsonStrToList(final String s) {
- final ArrayList<Object> list = CollectionUtils.newArrayList();
- final JsonReader reader = new JsonReader(new StringReader(s));
- try {
- reader.beginArray();
- while (reader.hasNext()) {
- reader.beginObject();
- while (reader.hasNext()) {
- final String name = reader.nextName();
- if (name.equals(INTEGER_CLASS_NAME)) {
- list.add(reader.nextInt());
- } else if (name.equals(STRING_CLASS_NAME)) {
- list.add(reader.nextString());
- } else {
- Log.w(TAG, "Invalid name: " + name);
- reader.skipValue();
- }
- }
- reader.endObject();
- }
- reader.endArray();
- return list;
- } catch (final IOException e) {
- } finally {
- close(reader);
- }
- return Collections.<Object>emptyList();
- }
-
- public static String listToJsonStr(final List<Object> list) {
- if (list == null || list.isEmpty()) {
- return EMPTY_STRING;
- }
- final StringWriter sw = new StringWriter();
- final JsonWriter writer = new JsonWriter(sw);
- try {
- writer.beginArray();
- for (final Object o : list) {
- writer.beginObject();
- if (o instanceof Integer) {
- writer.name(INTEGER_CLASS_NAME).value((Integer)o);
- } else if (o instanceof String) {
- writer.name(STRING_CLASS_NAME).value((String)o);
- }
- writer.endObject();
- }
- writer.endArray();
- return sw.toString();
- } catch (final IOException e) {
- } finally {
- close(writer);
- }
- return EMPTY_STRING;
- }
-
- private static void close(final Closeable closeable) {
- try {
- if (closeable != null) {
- closeable.close();
- }
- } catch (final IOException e) {
- // Ignore
- }
- }
}
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h
index ac05b215b..95ee74f99 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h
@@ -73,8 +73,8 @@ class BigramDictContent : public SparseTableDictContent {
bool copyBigramList(const int bigramListPos, const int toPos);
- bool flushToFile(const char *const dictDirPath) const {
- return flush(dictDirPath, Ver4DictConstants::BIGRAM_LOOKUP_TABLE_FILE_EXTENSION,
+ bool flushToFile(const char *const dictBasePath) const {
+ return flush(dictBasePath, Ver4DictConstants::BIGRAM_LOOKUP_TABLE_FILE_EXTENSION,
Ver4DictConstants::BIGRAM_CONTENT_TABLE_FILE_EXTENSION,
Ver4DictConstants::BIGRAM_FILE_EXTENSION);
}
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.cpp
index 01e406b74..749e3fe8c 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.cpp
@@ -71,7 +71,7 @@ bool ProbabilityDictContent::setProbabilityEntry(const int terminalId,
return writeEntry(probabilityEntry, entryPos);
}
-bool ProbabilityDictContent::flushToFile(const char *const dictDirPath) const {
+bool ProbabilityDictContent::flushToFile(const char *const dictBasePath) const {
if (getEntryPos(mSize) < getBuffer()->getTailPosition()) {
ProbabilityDictContent probabilityDictContentToWrite(mHasHistoricalInfo);
for (int i = 0; i < mSize; ++i) {
@@ -81,10 +81,10 @@ bool ProbabilityDictContent::flushToFile(const char *const dictDirPath) const {
return false;
}
}
- return probabilityDictContentToWrite.flush(dictDirPath,
+ return probabilityDictContentToWrite.flush(dictBasePath,
Ver4DictConstants::FREQ_FILE_EXTENSION);
} else {
- return flush(dictDirPath, Ver4DictConstants::FREQ_FILE_EXTENSION);
+ return flush(dictBasePath, Ver4DictConstants::FREQ_FILE_EXTENSION);
}
}
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.cpp
index eca69ec23..555217837 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.cpp
@@ -46,8 +46,8 @@ int ShortcutDictContent::getShortcutListHeadPos(const int terminalId) const {
return addressLookupTable->get(terminalId);
}
-bool ShortcutDictContent::flushToFile(const char *const dictDirPath) const {
- return flush(dictDirPath, Ver4DictConstants::SHORTCUT_LOOKUP_TABLE_FILE_EXTENSION,
+bool ShortcutDictContent::flushToFile(const char *const dictBasePath) const {
+ return flush(dictBasePath, Ver4DictConstants::SHORTCUT_LOOKUP_TABLE_FILE_EXTENSION,
Ver4DictConstants::SHORTCUT_CONTENT_TABLE_FILE_EXTENSION,
Ver4DictConstants::SHORTCUT_FILE_EXTENSION);
}
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.h
index 670e6eab6..a52214ca2 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.h
@@ -53,7 +53,7 @@ class ShortcutDictContent : public SparseTableDictContent {
// Returns head position of shortcut list for a PtNode specified by terminalId.
int getShortcutListHeadPos(const int terminalId) const;
- bool flushToFile(const char *const dictDirPath) const;
+ bool flushToFile(const char *const dictBasePath) const;
bool runGC(const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap,
const ShortcutDictContent *const originalShortcutDictContent);
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/single_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/single_dict_content.h
index 9512bdbb0..d8eedf36e 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/single_dict_content.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/single_dict_content.h
@@ -59,9 +59,9 @@ class SingleDictContent : public DictContent {
return &mExpandableContentBuffer;
}
- bool flush(const char *const dictDirPath, const char *const contentFileName) const {
- return DictFileWritingUtils::flushBufferToFileInDir(dictDirPath, contentFileName,
- &mExpandableContentBuffer);
+ bool flush(const char *const dictBasePath, const char *const contentFileNameSuffix) const {
+ return DictFileWritingUtils::flushBufferToFileWithSuffix(dictBasePath,
+ contentFileNameSuffix, &mExpandableContentBuffer);
}
private:
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/sparse_table_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/sparse_table_dict_content.cpp
index 84aceeffe..abb7d5fd2 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/sparse_table_dict_content.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/sparse_table_dict_content.cpp
@@ -18,18 +18,18 @@
namespace latinime {
-bool SparseTableDictContent::flush(const char *const dictDirPath,
- const char *const lookupTableFileName, const char *const addressTableFileName,
- const char *const contentFileName) const {
- if (!DictFileWritingUtils::flushBufferToFileInDir(dictDirPath, lookupTableFileName,
+bool SparseTableDictContent::flush(const char *const dictBasePath,
+ const char *const lookupTableFileNameSuffix, const char *const addressTableFileNameSuffix,
+ const char *const contentFileNameSuffix) const {
+ if (!DictFileWritingUtils::flushBufferToFileWithSuffix(dictBasePath, lookupTableFileNameSuffix,
&mExpandableLookupTableBuffer)){
return false;
}
- if (!DictFileWritingUtils::flushBufferToFileInDir(dictDirPath, addressTableFileName,
+ if (!DictFileWritingUtils::flushBufferToFileWithSuffix(dictBasePath, addressTableFileNameSuffix,
&mExpandableAddressTableBuffer)) {
return false;
}
- if (!DictFileWritingUtils::flushBufferToFileInDir(dictDirPath, contentFileName,
+ if (!DictFileWritingUtils::flushBufferToFileWithSuffix(dictBasePath, contentFileNameSuffix,
&mExpandableContentBuffer)) {
return false;
}
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.cpp
index 24f62cd4b..c889cf5d1 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.cpp
@@ -50,7 +50,7 @@ bool TerminalPositionLookupTable::setTerminalPtNodePosition(
Ver4DictConstants::TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE, getEntryPos(terminalId));
}
-bool TerminalPositionLookupTable::flushToFile(const char *const dictDirPath) const {
+bool TerminalPositionLookupTable::flushToFile(const char *const dictBasePath) const {
// If the used buffer size is smaller than the actual buffer size, regenerate the lookup
// table and write the new table to the file.
if (getEntryPos(mSize) < getBuffer()->getTailPosition()) {
@@ -63,12 +63,12 @@ bool TerminalPositionLookupTable::flushToFile(const char *const dictDirPath) con
return false;
}
}
- return lookupTableToWrite.flush(dictDirPath,
+ return lookupTableToWrite.flush(dictBasePath,
Ver4DictConstants::TERMINAL_ADDRESS_TABLE_FILE_EXTENSION);
} else {
// We can simply use this lookup table because the buffer size has not been
// changed.
- return flush(dictDirPath, Ver4DictConstants::TERMINAL_ADDRESS_TABLE_FILE_EXTENSION);
+ return flush(dictBasePath, Ver4DictConstants::TERMINAL_ADDRESS_TABLE_FILE_EXTENSION);
}
}
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h
index 283b40237..5a28f52fd 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h
@@ -44,7 +44,7 @@ class TerminalPositionLookupTable : public SingleDictContent {
return mSize;
}
- bool flushToFile(const char *const dictDirPath) const;
+ bool flushToFile(const char *const dictBasePath) const;
bool runGCTerminalIds(TerminalIdMap *const terminalIdMap);
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp
index d17d2d597..e2355407a 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp
@@ -17,6 +17,7 @@
#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h"
#include <cerrno>
+#include <cstring>
#include <sys/stat.h>
#include <sys/types.h>
@@ -52,34 +53,42 @@ bool Ver4DictBuffers::flushHeaderAndDictBuffers(const char *const dictDirPath,
AKLOGE("Cannot create directory: %s. errno: %d.", tmpDirPath, errno);
return false;
}
+ // Get dictionary base path.
+ const int dictNameBufSize = strlen(dictDirPath) + 1 /* terminator */;
+ char dictName[dictNameBufSize];
+ FileUtils::getBasename(dictDirPath, dictNameBufSize, dictName);
+ const int dictBasePathBufSize = FileUtils::getFilePathBufSize(tmpDirPath, dictName);
+ char dictBasePath[dictBasePathBufSize];
+ FileUtils::getFilePath(tmpDirPath, dictName, dictBasePathBufSize, dictBasePath);
+
// Write header file.
- if (!DictFileWritingUtils::flushBufferToFileInDir(tmpDirPath,
+ if (!DictFileWritingUtils::flushBufferToFileWithSuffix(dictBasePath,
Ver4DictConstants::HEADER_FILE_EXTENSION, headerBuffer)) {
- AKLOGE("Dictionary header file %s/%s cannot be written.", tmpDirPath,
+ AKLOGE("Dictionary header file %s%s cannot be written.", tmpDirPath,
Ver4DictConstants::HEADER_FILE_EXTENSION);
return false;
}
// Write trie file.
- if (!DictFileWritingUtils::flushBufferToFileInDir(tmpDirPath,
+ if (!DictFileWritingUtils::flushBufferToFileWithSuffix(dictBasePath,
Ver4DictConstants::TRIE_FILE_EXTENSION, &mExpandableTrieBuffer)) {
- AKLOGE("Dictionary trie file %s/%s cannot be written.", tmpDirPath,
+ AKLOGE("Dictionary trie file %s%s cannot be written.", tmpDirPath,
Ver4DictConstants::TRIE_FILE_EXTENSION);
return false;
}
// Write dictionary contents.
- if (!mTerminalPositionLookupTable.flushToFile(tmpDirPath)) {
+ if (!mTerminalPositionLookupTable.flushToFile(dictBasePath)) {
AKLOGE("Terminal position lookup table cannot be written. %s", tmpDirPath);
return false;
}
- if (!mProbabilityDictContent.flushToFile(tmpDirPath)) {
+ if (!mProbabilityDictContent.flushToFile(dictBasePath)) {
AKLOGE("Probability dict content cannot be written. %s", tmpDirPath);
return false;
}
- if (!mBigramDictContent.flushToFile(tmpDirPath)) {
+ if (!mBigramDictContent.flushToFile(dictBasePath)) {
AKLOGE("Bigram dict content cannot be written. %s", tmpDirPath);
return false;
}
- if (!mShortcutDictContent.flushToFile(tmpDirPath)) {
+ if (!mShortcutDictContent.flushToFile(dictBasePath)) {
AKLOGE("Shortcut dict content cannot be written. %s", tmpDirPath);
return false;
}
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp
index ec67c188f..442373b29 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp
@@ -83,11 +83,11 @@ const char *const DictFileWritingUtils::TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE =
return true;
}
-/* static */ bool DictFileWritingUtils::flushBufferToFileInDir(const char *const dirPath,
- const char *const fileName, const BufferWithExtendableBuffer *const buffer) {
- const int filePathBufSize = FileUtils::getFilePathBufSize(dirPath, fileName);
+/* static */ bool DictFileWritingUtils::flushBufferToFileWithSuffix(const char *const basePath,
+ const char *const suffix, const BufferWithExtendableBuffer *const buffer) {
+ const int filePathBufSize = FileUtils::getFilePathWithSuffixBufSize(basePath, suffix);
char filePath[filePathBufSize];
- FileUtils::getFilePath(dirPath, fileName, filePathBufSize, filePath);
+ FileUtils::getFilePathWithSuffix(basePath, suffix, filePathBufSize, filePath);
return flushBufferToFile(filePath, buffer);
}
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h
index ffd9db623..bdf9fd63c 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h
@@ -37,7 +37,7 @@ class DictFileWritingUtils {
BufferWithExtendableBuffer *const dictHeader,
BufferWithExtendableBuffer *const dictBody);
- static bool flushBufferToFileInDir(const char *const dirPath, const char *const fileName,
+ static bool flushBufferToFileWithSuffix(const char *const basePath, const char *const suffix,
const BufferWithExtendableBuffer *const buffer);
private:
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.cpp
index 34da76903..49ae7f156 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.cpp
@@ -20,6 +20,7 @@
#include <cstring>
#include <dirent.h>
#include <fcntl.h>
+#include <libgen.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
@@ -138,4 +139,19 @@ namespace latinime {
}
}
+/* static */ void FileUtils::getBasename(const char *const filePath,
+ const int outNameBufSize, char *const outName) {
+ const int filePathBufSize = strlen(filePath) + 1 /* terminator */;
+ char filePathBuf[filePathBufSize];
+ snprintf(filePathBuf, filePathBufSize, "%s", filePath);
+ const char *const baseName = basename(filePathBuf);
+ const int baseNameLength = strlen(baseName);
+ if (baseNameLength >= outNameBufSize) {
+ AKLOGE("outNameBufSize is too small. dirPath: %s, outNameBufSize: %d",
+ filePath, outNameBufSize);
+ return;
+ }
+ snprintf(outName, baseNameLength + 1 /* terminator */, "%s", baseName);
+}
+
} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.h
index e55837337..3e84a3038 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.h
@@ -48,6 +48,9 @@ class FileUtils {
static void getDirPath(const char *const filePath, const int dirPathBufSize,
char *const outDirPath);
+ static void getBasename(const char *const filePath, const int outNameBufSize,
+ char *const outName);
+
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(FileUtils);
};
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
index d76a55283..931ba7d3c 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
@@ -122,12 +122,13 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase {
FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE);
attributeMap.put(FormatSpec.FileHeader.HAS_HISTORICAL_INFO_ATTRIBUTE,
FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE);
+ final String headerFileName = file.getName() + FormatSpec.HEADER_FILE_EXTENSION;
if (BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(),
FormatSpec.VERSION4, attributeMap)) {
- return new File(file, FormatSpec.HEADER_FILE_EXTENSION);
+ return new File(file, headerFileName);
} else {
throw new IOException("Empty dictionary " + file.getAbsolutePath() + " "
- + FormatSpec.HEADER_FILE_EXTENSION + " cannot be created.");
+ + headerFileName + " cannot be created.");
}
}
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
index c14840258..d9d4a5584 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
@@ -70,12 +70,13 @@ public class BinaryDictionaryTests extends AndroidTestCase {
Map<String, String> attributeMap = new HashMap<String, String>();
attributeMap.put(FormatSpec.FileHeader.SUPPORTS_DYNAMIC_UPDATE_ATTRIBUTE,
FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE);
+ final String headerFileName = file.getName() + FormatSpec.HEADER_FILE_EXTENSION;
if (BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(),
FormatSpec.VERSION4, attributeMap)) {
- return new File(file, FormatSpec.HEADER_FILE_EXTENSION);
+ return new File(file, headerFileName);
} else {
throw new IOException("Empty dictionary " + file.getAbsolutePath() + " "
- + FormatSpec.HEADER_FILE_EXTENSION + " cannot be created.");
+ + headerFileName + " cannot be created.");
}
}
diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
index 717b04f75..f0fe3f2e1 100644
--- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
@@ -20,7 +20,6 @@ import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
-import com.android.inputmethod.latin.ExpandableBinaryDictionary;
import com.android.inputmethod.latin.utils.CollectionUtils;
import java.io.File;
@@ -137,8 +136,7 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
Log.d(TAG, "This test can be used for profiling.");
Log.d(TAG, "Usage: please set UserHistoryDictionary.PROFILE_SAVE_RESTORE to true.");
final String testFilenameSuffix = "test_random_words" + System.currentTimeMillis();
- final String fileName = UserHistoryDictionary.NAME + "." + testFilenameSuffix
- + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
+ final String fileName = UserHistoryDictionary.NAME + "." + testFilenameSuffix;
final int numberOfWords = 1000;
final Random random = new Random(123456);
@@ -172,8 +170,7 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
// Create filename suffixes for this test.
for (int i = 0; i < numberOfLanguages; i++) {
testFilenameSuffixes[i] = "test_switching_languages" + i;
- final String fileName = UserHistoryDictionary.NAME + "." +
- testFilenameSuffixes[i] + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
+ final String fileName = UserHistoryDictionary.NAME + "." + testFilenameSuffixes[i];
dictFiles[i] = new File(getContext().getFilesDir(), fileName);
clearHistory(testFilenameSuffixes[i]);
}
@@ -217,8 +214,7 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
} finally {
Log.d(TAG, "waiting for writing ...");
waitForWriting(testFilenameSuffix);
- final String fileName = UserHistoryDictionary.NAME + "." + testFilenameSuffix
- + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
+ final String fileName = UserHistoryDictionary.NAME + "." + testFilenameSuffix;
final File dictFile = new File(getContext().getFilesDir(), fileName);
if (dictFile != null) {
assertTrue(dictFile.exists());
diff --git a/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java
index 21fcf1117..2123e84e8 100644
--- a/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java
@@ -26,7 +26,7 @@ import java.util.List;
import java.util.Locale;
@SmallTest
-public class StringUtilsTests extends AndroidTestCase {
+public class StringAndJsonUtilsTests extends AndroidTestCase {
public void testContainsInArray() {
assertFalse("empty array", StringUtils.containsInArray("key", new String[0]));
assertFalse("not in 1 element", StringUtils.containsInArray("key", new String[] {
@@ -292,11 +292,11 @@ public class StringUtilsTests extends AndroidTestCase {
assertTrue(bytesStr.equals(bytesStr2));
}
- public void testJsonStringUtils() {
+ public void testJsonUtils() {
final Object[] objs = new Object[] { 1, "aaa", "bbb", 3 };
final List<Object> objArray = Arrays.asList(objs);
- final String str = StringUtils.listToJsonStr(objArray);
- final List<Object> newObjArray = StringUtils.jsonStrToList(str);
+ final String str = JsonUtils.listToJsonStr(objArray);
+ final List<Object> newObjArray = JsonUtils.jsonStrToList(str);
for (int i = 0; i < objs.length; ++i) {
assertEquals(objs[i], newObjArray.get(i));
}
diff --git a/tools/make-keyboard-text/res/values-es/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-es/donottranslate-more-keys.xml
index deae87939..f4fe7f787 100644
--- a/tools/make-keyboard-text/res/values-es/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-es/donottranslate-more-keys.xml
@@ -67,8 +67,6 @@
U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
U+010D: "č" LATIN SMALL LETTER C WITH CARON -->
<string name="more_keys_for_c">&#x00E7;,&#x0107;,&#x010D;</string>
- <!-- U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE -->
- <string name="keylabel_for_spanish_row2_10">&#x00F1;</string>
<!-- U+00A1: "¡" INVERTED EXCLAMATION MARK
U+00BF: "¿" INVERTED QUESTION MARK -->
<string name="more_keys_for_punctuation">"!fixedColumnOrder!9,&#x00A1;,;,/,(,),#,!,\\,,\?,&#x00BF;,&amp;,\\%,+,\",-,:,',\@"</string>
diff --git a/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml
index c1f3e4cea..ceb46dcd2 100644
--- a/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml
@@ -189,7 +189,8 @@
<string name="keylabel_for_w">w</string>
<string name="keylabel_for_y">y</string>
<string name="keylabel_for_x">x</string>
- <string name="keylabel_for_spanish_row2_10"></string>
+ <!-- U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE -->
+ <string name="keylabel_for_spanish_row2_10">&#x00F1;</string>
<string name="more_keys_for_am_pm">!fixedColumnOrder!2,!hasLabels!,\@string/label_time_am,\@string/label_time_pm</string>
<string name="settings_as_more_key">!icon/settings_key|!code/key_settings</string>
<string name="shortcut_as_more_key">!icon/shortcut_key|!code/key_shortcut</string>