aboutsummaryrefslogtreecommitdiffstats
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java13
-rw-r--r--java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java14
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/ActionBatch.java21
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java10
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java6
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java117
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DownloadManagerWrapper.java109
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java58
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/MetadataHandler.java17
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java32
-rw-r--r--java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java47
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java4
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java12
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardView.java1
-rw-r--r--java/src/com/android/inputmethod/keyboard/MainKeyboardView.java8
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java23
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java74
-rw-r--r--java/src/com/android/inputmethod/latin/InputAttributes.java289
-rw-r--r--java/src/com/android/inputmethod/latin/InputView.java119
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java208
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java24
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java17
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java76
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java186
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java193
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java128
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/DictDecoder.java12
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/DictEncoder.java4
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/FormatSpec.java6
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/PtNodeInfo.java4
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java103
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java42
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java5
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java3
-rw-r--r--java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java8
-rw-r--r--java/src/com/android/inputmethod/latin/settings/Settings.java1
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SettingsFragment.java1
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java8
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java63
-rw-r--r--java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java17
-rw-r--r--java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java2
-rw-r--r--java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java14
-rw-r--r--java/src/com/android/inputmethod/latin/utils/SpannableStringUtils.java13
-rw-r--r--java/src/com/android/inputmethod/latin/utils/TextRange.java6
44 files changed, 949 insertions, 1169 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
index d0d5399c6..0043b7844 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
@@ -204,19 +204,6 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp
}
/**
- * Intercepts touch events before dispatch when touch exploration is turned on in ICS and
- * higher.
- *
- * @param event The motion event being dispatched.
- * @return {@code true} if the event is handled
- */
- public boolean dispatchTouchEvent(final MotionEvent event) {
- // To avoid accidental key presses during touch exploration, always drop
- // touch events generated by the user.
- return false;
- }
-
- /**
* Receives hover events when touch exploration is turned on in SDK versions ICS and higher.
*
* @param event The hover event.
diff --git a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java
index b119d6c82..4ea7fb888 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java
@@ -19,7 +19,10 @@ package com.android.inputmethod.compat;
import android.os.Build;
import android.view.inputmethod.InputMethodSubtype;
+import com.android.inputmethod.latin.Constants;
+
import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
public final class InputMethodSubtypeCompatUtils {
private static final String TAG = InputMethodSubtypeCompatUtils.class.getSimpleName();
@@ -37,6 +40,12 @@ public final class InputMethodSubtypeCompatUtils {
}
}
}
+
+ // Note that {@link InputMethodSubtype#isAsciiCapable()} has been introduced in API level 19
+ // (Build.VERSION_CODE.KITKAT).
+ private static final Method METHOD_isAsciiCapable = CompatUtils.getMethod(
+ InputMethodSubtype.class, "isAsciiCapable");
+
private InputMethodSubtypeCompatUtils() {
// This utility class is not publicly instantiable.
}
@@ -53,4 +62,9 @@ public final class InputMethodSubtypeCompatUtils {
nameId, iconId, locale, mode, extraValue, isAuxiliary,
overridesImplicitlyEnabledSubtype, id);
}
+
+ public static boolean isAsciiCapable(final InputMethodSubtype subtype) {
+ return (Boolean)CompatUtils.invoke(subtype, false, METHOD_isAsciiCapable)
+ || subtype.containsExtraValueKey(Constants.Subtype.ExtraValue.ASCII_CAPABLE);
+ }
}
diff --git a/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java b/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java
index d5e638e7e..706bdea8e 100644
--- a/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java
+++ b/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java
@@ -117,16 +117,11 @@ public final class ActionBatch {
final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db,
mWordList.mId, mWordList.mVersion);
final int status = values.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
- final DownloadManager manager =
- (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
+ final DownloadManagerWrapper manager = new DownloadManagerWrapper(context);
if (MetadataDbHelper.STATUS_DOWNLOADING == status) {
// The word list is still downloading. Cancel the download and revert the
// word list status to "available".
- if (null != manager) {
- // DownloadManager is disabled (or not installed?). We can't cancel - there
- // is nothing we can do. We still need to mark the entry as available.
- manager.remove(values.getAsLong(MetadataDbHelper.PENDINGID_COLUMN));
- }
+ manager.remove(values.getAsLong(MetadataDbHelper.PENDINGID_COLUMN));
MetadataDbHelper.markEntryAsAvailable(db, mWordList.mId, mWordList.mVersion);
} else if (MetadataDbHelper.STATUS_AVAILABLE != status) {
// Should never happen
@@ -136,9 +131,6 @@ public final class ActionBatch {
// Download it.
DebugLogUtils.l("Upgrade word list, downloading", mWordList.mRemoteFilename);
- // TODO: if DownloadManager is disabled or not installed, download by ourselves
- if (null == manager) return;
-
// This is an upgraded word list: we should download it.
// Adding a disambiguator to circumvent a bug in older versions of DownloadManager.
// DownloadManager also stupidly cuts the extension to replace with its own that it
@@ -293,13 +285,8 @@ public final class ActionBatch {
}
// The word list is still downloading. Cancel the download and revert the
// word list status to "available".
- final DownloadManager manager =
- (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
- if (null != manager) {
- // If we can't cancel the download because DownloadManager is not available,
- // we still need to mark the entry as available.
- manager.remove(values.getAsLong(MetadataDbHelper.PENDINGID_COLUMN));
- }
+ final DownloadManagerWrapper manager = new DownloadManagerWrapper(context);
+ manager.remove(values.getAsLong(MetadataDbHelper.PENDINGID_COLUMN));
MetadataDbHelper.markEntryAsAvailable(db, mWordList.mId, mWordList.mVersion);
}
}
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java
index 384ee3e07..2623eff56 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java
@@ -107,26 +107,22 @@ public class DictionaryDownloadProgressBar extends ProgressBar {
private class UpdaterThread extends Thread {
private final static int REPORT_PERIOD = 150; // how often to report progress, in ms
- final DownloadManager mDownloadManager;
+ final DownloadManagerWrapper mDownloadManagerWrapper;
final int mId;
public UpdaterThread(final Context context, final int id) {
super();
- mDownloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
+ mDownloadManagerWrapper = new DownloadManagerWrapper(context);
mId = id;
}
@Override
public void run() {
try {
- // It's almost impossible that mDownloadManager is null (it would mean it has been
- // disabled between pressing the 'install' button and displaying the progress
- // bar), but just in case.
- if (null == mDownloadManager) return;
final UpdateHelper updateHelper = new UpdateHelper();
final Query query = new Query().setFilterById(mId);
int lastProgress = 0;
setIndeterminate(true);
while (!isInterrupted()) {
- final Cursor cursor = mDownloadManager.query(query);
+ final Cursor cursor = mDownloadManagerWrapper.query(query);
if (null == cursor) {
// Can't contact DownloadManager: this should never happen.
return;
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java
index 1d9b9991e..80def701d 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java
@@ -350,7 +350,8 @@ public final class DictionaryProvider extends ContentProvider {
clientId);
if (null == results) {
return Collections.<WordListInfo>emptyList();
- } else {
+ }
+ try {
final HashMap<String, WordListInfo> dicts = new HashMap<String, WordListInfo>();
final int idIndex = results.getColumnIndex(MetadataDbHelper.WORDLISTID_COLUMN);
final int localeIndex = results.getColumnIndex(MetadataDbHelper.LOCALE_COLUMN);
@@ -416,8 +417,9 @@ public final class DictionaryProvider extends ContentProvider {
}
} while (results.moveToNext());
}
- results.close();
return Collections.unmodifiableCollection(dicts.values());
+ } finally {
+ results.close();
}
}
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java
index d18639741..dae2f22a4 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java
@@ -283,63 +283,70 @@ public final class DictionarySettingsFragment extends PreferenceFragment
final ArrayList<Preference> result = new ArrayList<Preference>();
result.add(createErrorMessage(activity, R.string.cannot_connect_to_dict_service));
return result;
- } else if (!cursor.moveToFirst()) {
- final ArrayList<Preference> result = new ArrayList<Preference>();
- result.add(createErrorMessage(activity, R.string.no_dictionaries_available));
- cursor.close();
- return result;
- } else {
- final String systemLocaleString = Locale.getDefault().toString();
- final TreeMap<String, WordListPreference> prefMap =
- new TreeMap<String, WordListPreference>();
- final int idIndex = cursor.getColumnIndex(MetadataDbHelper.WORDLISTID_COLUMN);
- final int versionIndex = cursor.getColumnIndex(MetadataDbHelper.VERSION_COLUMN);
- final int localeIndex = cursor.getColumnIndex(MetadataDbHelper.LOCALE_COLUMN);
- final int descriptionIndex = cursor.getColumnIndex(MetadataDbHelper.DESCRIPTION_COLUMN);
- final int statusIndex = cursor.getColumnIndex(MetadataDbHelper.STATUS_COLUMN);
- final int filesizeIndex = cursor.getColumnIndex(MetadataDbHelper.FILESIZE_COLUMN);
- do {
- final String wordlistId = cursor.getString(idIndex);
- final int version = cursor.getInt(versionIndex);
- final String localeString = cursor.getString(localeIndex);
- final Locale locale = new Locale(localeString);
- final String description = cursor.getString(descriptionIndex);
- final int status = cursor.getInt(statusIndex);
- final int matchLevel = LocaleUtils.getMatchLevel(systemLocaleString, localeString);
- final String matchLevelString = LocaleUtils.getMatchLevelSortedString(matchLevel);
- final int filesize = cursor.getInt(filesizeIndex);
- // The key is sorted in lexicographic order, according to the match level, then
- // the description.
- final String key = matchLevelString + "." + description + "." + wordlistId;
- final WordListPreference existingPref = prefMap.get(key);
- if (null == existingPref || existingPref.hasPriorityOver(status)) {
- final WordListPreference oldPreference = mCurrentPreferenceMap.get(key);
- final WordListPreference pref;
- if (null != oldPreference
- && oldPreference.mVersion == version
- && oldPreference.hasStatus(status)
- && oldPreference.mLocale.equals(locale)) {
- // If the old preference has all the new attributes, reuse it. Ideally, we
- // should reuse the old pref even if its status is different and call
- // setStatus here, but setStatus calls Preference#setSummary() which needs
- // to be done on the UI thread and we're not on the UI thread here. We
- // could do all this work on the UI thread, but in this case it's probably
- // lighter to stay on a background thread and throw this old preference out.
- pref = oldPreference;
- } else {
- // Otherwise, discard it and create a new one instead.
- // TODO: when the status is different from the old one, we need to
- // animate the old one out before animating the new one in.
- pref = new WordListPreference(activity, mDictionaryListInterfaceState,
- mClientId, wordlistId, version, locale, description, status,
- filesize);
+ }
+ try {
+ if (!cursor.moveToFirst()) {
+ final ArrayList<Preference> result = new ArrayList<Preference>();
+ result.add(createErrorMessage(activity, R.string.no_dictionaries_available));
+ return result;
+ } else {
+ final String systemLocaleString = Locale.getDefault().toString();
+ final TreeMap<String, WordListPreference> prefMap =
+ new TreeMap<String, WordListPreference>();
+ final int idIndex = cursor.getColumnIndex(MetadataDbHelper.WORDLISTID_COLUMN);
+ final int versionIndex = cursor.getColumnIndex(MetadataDbHelper.VERSION_COLUMN);
+ final int localeIndex = cursor.getColumnIndex(MetadataDbHelper.LOCALE_COLUMN);
+ final int descriptionIndex =
+ cursor.getColumnIndex(MetadataDbHelper.DESCRIPTION_COLUMN);
+ final int statusIndex = cursor.getColumnIndex(MetadataDbHelper.STATUS_COLUMN);
+ final int filesizeIndex = cursor.getColumnIndex(MetadataDbHelper.FILESIZE_COLUMN);
+ do {
+ final String wordlistId = cursor.getString(idIndex);
+ final int version = cursor.getInt(versionIndex);
+ final String localeString = cursor.getString(localeIndex);
+ final Locale locale = new Locale(localeString);
+ final String description = cursor.getString(descriptionIndex);
+ final int status = cursor.getInt(statusIndex);
+ final int matchLevel =
+ LocaleUtils.getMatchLevel(systemLocaleString, localeString);
+ final String matchLevelString =
+ LocaleUtils.getMatchLevelSortedString(matchLevel);
+ final int filesize = cursor.getInt(filesizeIndex);
+ // The key is sorted in lexicographic order, according to the match level, then
+ // the description.
+ final String key = matchLevelString + "." + description + "." + wordlistId;
+ final WordListPreference existingPref = prefMap.get(key);
+ if (null == existingPref || existingPref.hasPriorityOver(status)) {
+ final WordListPreference oldPreference = mCurrentPreferenceMap.get(key);
+ final WordListPreference pref;
+ if (null != oldPreference
+ && oldPreference.mVersion == version
+ && oldPreference.hasStatus(status)
+ && oldPreference.mLocale.equals(locale)) {
+ // If the old preference has all the new attributes, reuse it. Ideally,
+ // we should reuse the old pref even if its status is different and call
+ // setStatus here, but setStatus calls Preference#setSummary() which
+ // needs to be done on the UI thread and we're not on the UI thread
+ // here. We could do all this work on the UI thread, but in this case
+ // it's probably lighter to stay on a background thread and throw this
+ // old preference out.
+ pref = oldPreference;
+ } else {
+ // Otherwise, discard it and create a new one instead.
+ // TODO: when the status is different from the old one, we need to
+ // animate the old one out before animating the new one in.
+ pref = new WordListPreference(activity, mDictionaryListInterfaceState,
+ mClientId, wordlistId, version, locale, description, status,
+ filesize);
+ }
+ prefMap.put(key, pref);
}
- prefMap.put(key, pref);
- }
- } while (cursor.moveToNext());
+ } while (cursor.moveToNext());
+ mCurrentPreferenceMap = prefMap;
+ return prefMap.values();
+ }
+ } finally {
cursor.close();
- mCurrentPreferenceMap = prefMap;
- return prefMap.values();
}
}
diff --git a/java/src/com/android/inputmethod/dictionarypack/DownloadManagerWrapper.java b/java/src/com/android/inputmethod/dictionarypack/DownloadManagerWrapper.java
new file mode 100644
index 000000000..75cc7d4cb
--- /dev/null
+++ b/java/src/com/android/inputmethod/dictionarypack/DownloadManagerWrapper.java
@@ -0,0 +1,109 @@
+/*
+ * 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;
+
+import android.app.DownloadManager;
+import android.app.DownloadManager.Query;
+import android.app.DownloadManager.Request;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.FileNotFoundException;
+
+/**
+ * A class to help with calling DownloadManager methods.
+ *
+ * Mostly, the problem here is that most methods from DownloadManager may throw SQL exceptions if
+ * they can't open the database on disk. We want to avoid crashing in these cases but can't do
+ * much more, so this class insulates the callers from these. SQLiteException also inherit from
+ * RuntimeException so they are unchecked :(
+ * While we're at it, we also insulate callers from the cases where DownloadManager is disabled,
+ * and getSystemService returns null.
+ */
+public class DownloadManagerWrapper {
+ private final static String TAG = DownloadManagerWrapper.class.getSimpleName();
+ private final DownloadManager mDownloadManager;
+
+ public DownloadManagerWrapper(final Context context) {
+ this((DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE));
+ }
+
+ private DownloadManagerWrapper(final DownloadManager downloadManager) {
+ mDownloadManager = downloadManager;
+ }
+
+ public void remove(final long... ids) {
+ try {
+ if (null != mDownloadManager) {
+ mDownloadManager.remove(ids);
+ }
+ } catch (SQLiteException e) {
+ // We couldn't remove the file from DownloadManager. Apparently, the database can't
+ // be opened. It may be a problem with file system corruption. In any case, there is
+ // not much we can do apart from avoiding crashing.
+ Log.e(TAG, "Can't remove files with ID " + ids + " from download manager", e);
+ } catch (IllegalArgumentException e) {
+ // Not sure how this can happen, but it could be another case where the provider
+ // is disabled. Or it could be a bug in older versions of the framework.
+ Log.e(TAG, "Can't find the content URL for DownloadManager?", e);
+ }
+ }
+
+ public ParcelFileDescriptor openDownloadedFile(final long fileId) throws FileNotFoundException {
+ try {
+ if (null != mDownloadManager) {
+ return mDownloadManager.openDownloadedFile(fileId);
+ }
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Can't open downloaded file with ID " + fileId, e);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Can't find the content URL for DownloadManager?", e);
+ }
+ // We come here if mDownloadManager is null or if an exception was thrown.
+ throw new FileNotFoundException();
+ }
+
+ public Cursor query(final Query query) {
+ try {
+ if (null != mDownloadManager) {
+ return mDownloadManager.query(query);
+ }
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Can't query the download manager", e);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Can't find the content URL for DownloadManager?", e);
+ }
+ // We come here if mDownloadManager is null or if an exception was thrown.
+ return null;
+ }
+
+ public long enqueue(final Request request) {
+ try {
+ if (null != mDownloadManager) {
+ return mDownloadManager.enqueue(request);
+ }
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Can't enqueue a request with the download manager", e);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Can't find the content URL for DownloadManager?", e);
+ }
+ return 0;
+ }
+}
diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java
index ff5aba6d8..8badaf4b9 100644
--- a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java
+++ b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java
@@ -533,12 +533,17 @@ public class MetadataDbHelper extends SQLiteOpenHelper {
PENDINGID_COLUMN + "= ?",
new String[] { Long.toString(id) },
null, null, null);
- // There should never be more than one result. If because of some bug there are, returning
- // only one result is the right thing to do, because we couldn't handle several anyway
- // and we should still handle one.
- final ContentValues result = getFirstLineAsContentValues(cursor);
- cursor.close();
- return result;
+ if (null == cursor) {
+ return null;
+ }
+ try {
+ // There should never be more than one result. If because of some bug there are,
+ // returning only one result is the right thing to do, because we couldn't handle
+ // several anyway and we should still handle one.
+ return getFirstLineAsContentValues(cursor);
+ } finally {
+ cursor.close();
+ }
}
/**
@@ -559,11 +564,16 @@ public class MetadataDbHelper extends SQLiteOpenHelper {
new String[] { id, Integer.toString(STATUS_INSTALLED),
Integer.toString(STATUS_DELETING) },
null, null, null);
- // There should only be one result, but if there are several, we can't tell which
- // is the best, so we just return the first one.
- final ContentValues result = getFirstLineAsContentValues(cursor);
- cursor.close();
- return result;
+ if (null == cursor) {
+ return null;
+ }
+ try {
+ // There should only be one result, but if there are several, we can't tell which
+ // is the best, so we just return the first one.
+ return getFirstLineAsContentValues(cursor);
+ } finally {
+ cursor.close();
+ }
}
/**
@@ -622,10 +632,15 @@ public class MetadataDbHelper extends SQLiteOpenHelper {
METADATA_TABLE_COLUMNS,
WORDLISTID_COLUMN + "= ? AND " + VERSION_COLUMN + "= ?",
new String[] { id, Integer.toString(version) }, null, null, null);
- // This is a lookup by primary key, so there can't be more than one result.
- final ContentValues result = getFirstLineAsContentValues(cursor);
- cursor.close();
- return result;
+ if (null == cursor) {
+ return null;
+ }
+ try {
+ // This is a lookup by primary key, so there can't be more than one result.
+ return getFirstLineAsContentValues(cursor);
+ } finally {
+ cursor.close();
+ }
}
/**
@@ -641,10 +656,15 @@ public class MetadataDbHelper extends SQLiteOpenHelper {
METADATA_TABLE_COLUMNS,
WORDLISTID_COLUMN + "= ?",
new String[] { id }, null, null, VERSION_COLUMN + " DESC", "1");
- // This is a lookup by primary key, so there can't be more than one result.
- final ContentValues result = getFirstLineAsContentValues(cursor);
- cursor.close();
- return result;
+ if (null == cursor) {
+ return null;
+ }
+ try {
+ // This is a lookup by primary key, so there can't be more than one result.
+ return getFirstLineAsContentValues(cursor);
+ } finally {
+ cursor.close();
+ }
}
/**
diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataHandler.java b/java/src/com/android/inputmethod/dictionarypack/MetadataHandler.java
index a0147b6d6..5c2289911 100644
--- a/java/src/com/android/inputmethod/dictionarypack/MetadataHandler.java
+++ b/java/src/com/android/inputmethod/dictionarypack/MetadataHandler.java
@@ -44,8 +44,7 @@ public class MetadataHandler {
*/
private static List<WordListMetadata> makeMetadataObject(final Cursor results) {
final ArrayList<WordListMetadata> buildingMetadata = new ArrayList<WordListMetadata>();
-
- if (results.moveToFirst()) {
+ if (null != results && results.moveToFirst()) {
final int localeColumn = results.getColumnIndex(MetadataDbHelper.LOCALE_COLUMN);
final int typeColumn = results.getColumnIndex(MetadataDbHelper.TYPE_COLUMN);
final int descriptionColumn =
@@ -61,7 +60,6 @@ public class MetadataHandler {
final int versionIndex = results.getColumnIndex(MetadataDbHelper.VERSION_COLUMN);
final int formatVersionIndex =
results.getColumnIndex(MetadataDbHelper.FORMATVERSION_COLUMN);
-
do {
buildingMetadata.add(new WordListMetadata(results.getString(idIndex),
results.getInt(typeColumn),
@@ -75,8 +73,6 @@ public class MetadataHandler {
results.getInt(formatVersionIndex),
0, results.getString(localeColumn)));
} while (results.moveToNext());
-
- results.close();
}
return Collections.unmodifiableList(buildingMetadata);
}
@@ -92,9 +88,14 @@ public class MetadataHandler {
// If clientId is null, we get a cursor on the default database (see
// MetadataDbHelper#getInstance() for more on this)
final Cursor results = MetadataDbHelper.queryCurrentMetadata(context, clientId);
- final List<WordListMetadata> resultList = makeMetadataObject(results);
- results.close();
- return resultList;
+ // If null, we should return makeMetadataObject(null), so we go through.
+ try {
+ return makeMetadataObject(results);
+ } finally {
+ if (null != results) {
+ results.close();
+ }
+ }
}
/**
diff --git a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
index 0e7c3bb7e..dcff490db 100644
--- a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
+++ b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
@@ -249,13 +249,7 @@ public final class UpdateHandler {
metadataRequest.setVisibleInDownloadsUi(
res.getBoolean(R.bool.metadata_downloads_visible_in_download_UI));
- final DownloadManager manager =
- (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
- if (null == manager) {
- // Download manager is not installed or disabled.
- // TODO: fall back to self-managed download?
- return;
- }
+ final DownloadManagerWrapper manager = new DownloadManagerWrapper(context);
cancelUpdateWithDownloadManager(context, metadataUri, manager);
final long downloadId;
synchronized (sSharedIdProtector) {
@@ -278,10 +272,10 @@ public final class UpdateHandler {
*
* @param context the context to open the database on
* @param metadataUri the URI to cancel
- * @param manager an instance of DownloadManager
+ * @param manager an wrapped instance of DownloadManager
*/
private static void cancelUpdateWithDownloadManager(final Context context,
- final String metadataUri, final DownloadManager manager) {
+ final String metadataUri, final DownloadManagerWrapper manager) {
synchronized (sSharedIdProtector) {
final long metadataDownloadId =
MetadataDbHelper.getMetadataDownloadIdForURI(context, metadataUri);
@@ -306,10 +300,9 @@ public final class UpdateHandler {
* @param clientId the ID of the client we want to cancel the update of
*/
public static void cancelUpdate(final Context context, final String clientId) {
- final DownloadManager manager =
- (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
+ final DownloadManagerWrapper manager = new DownloadManagerWrapper(context);
final String metadataUri = MetadataDbHelper.getMetadataUriAsString(context, clientId);
- if (null != manager) cancelUpdateWithDownloadManager(context, metadataUri, manager);
+ cancelUpdateWithDownloadManager(context, metadataUri, manager);
}
/**
@@ -323,15 +316,15 @@ public final class UpdateHandler {
* download request id, which is not known before submitting the request to the download
* manager. Hence, it only updates the relevant line.
*
- * @param manager the download manager service to register the request with.
+ * @param manager a wrapped download manager service to register the request with.
* @param request the request to register.
* @param db the metadata database.
* @param id the id of the word list.
* @param version the version of the word list.
* @return the download id returned by the download manager.
*/
- public static long registerDownloadRequest(final DownloadManager manager, final Request request,
- final SQLiteDatabase db, final String id, final int version) {
+ public static long registerDownloadRequest(final DownloadManagerWrapper manager,
+ final Request request, final SQLiteDatabase db, final String id, final int version) {
DebugLogUtils.l("RegisterDownloadRequest for word list id : ", id, ", version ", version);
final long downloadId;
synchronized (sSharedIdProtector) {
@@ -345,8 +338,8 @@ public final class UpdateHandler {
/**
* Retrieve information about a specific download from DownloadManager.
*/
- private static CompletedDownloadInfo getCompletedDownloadInfo(final DownloadManager manager,
- final long downloadId) {
+ private static CompletedDownloadInfo getCompletedDownloadInfo(
+ final DownloadManagerWrapper manager, final long downloadId) {
final Query query = new Query().setFilterById(downloadId);
final Cursor cursor = manager.query(query);
@@ -425,8 +418,7 @@ public final class UpdateHandler {
DebugLogUtils.l("DownloadFinished with id", fileId);
if (NOT_AN_ID == fileId) return; // Spurious wake-up: ignore
- final DownloadManager manager =
- (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
+ final DownloadManagerWrapper manager = new DownloadManagerWrapper(context);
final CompletedDownloadInfo downloadInfo = getCompletedDownloadInfo(manager, fileId);
final ArrayList<DownloadRecord> recordList =
@@ -517,7 +509,7 @@ public final class UpdateHandler {
}
private static boolean handleDownloadedFile(final Context context,
- final DownloadRecord downloadRecord, final DownloadManager manager,
+ final DownloadRecord downloadRecord, final DownloadManagerWrapper manager,
final long fileId) {
try {
// {@link handleWordList(Context,InputStream,ContentValues)}.
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
index c34464314..4c53b528f 100644
--- a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
+++ b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
@@ -25,6 +25,7 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Rect;
+import android.graphics.Typeface;
import android.os.Build;
import android.os.CountDownTimer;
import android.preference.PreferenceManager;
@@ -34,6 +35,7 @@ import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
+import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -84,6 +86,8 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
private EmojiPalettesAdapter mEmojiPalettesAdapter;
private final EmojiLayoutParams mEmojiLayoutParams;
+ private TextView mAlphabetKeyLeft;
+ private TextView mAlphabetKeyRight;
private TabHost mTabHost;
private ViewPager mEmojiPager;
private int mCurrentPagerPosition = 0;
@@ -487,20 +491,23 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
deleteKey.setTag(Constants.CODE_DELETE);
deleteKey.setOnTouchListener(mDeleteKeyOnTouchListener);
- // alphabetKey, alphabetKey2, and spaceKey depend on {@link View.OnClickListener} as well as
- // {@link View.OnTouchListener}. {@link View.OnTouchListener} is used as the trigger of
- // key-press, while {@link View.OnClickListener} is used as the trigger of key-release which
- // does not occur if the event is canceled by moving off the finger from the view.
- final ImageView alphabetKey = (ImageView)findViewById(R.id.emoji_keyboard_alphabet);
- alphabetKey.setBackgroundResource(mEmojiFunctionalKeyBackgroundId);
- alphabetKey.setTag(Constants.CODE_ALPHA_FROM_EMOJI);
- alphabetKey.setOnTouchListener(this);
- alphabetKey.setOnClickListener(this);
- final ImageView alphabetKey2 = (ImageView)findViewById(R.id.emoji_keyboard_alphabet2);
- alphabetKey2.setBackgroundResource(mEmojiFunctionalKeyBackgroundId);
- alphabetKey2.setTag(Constants.CODE_ALPHA_FROM_EMOJI);
- alphabetKey2.setOnTouchListener(this);
- alphabetKey2.setOnClickListener(this);
+ // {@link #mAlphabetKeyLeft}, {@link #mAlphabetKeyRight, and spaceKey depend on
+ // {@link View.OnClickListener} as well as {@link View.OnTouchListener}.
+ // {@link View.OnTouchListener} is used as the trigger of key-press, while
+ // {@link View.OnClickListener} is used as the trigger of key-release which does not occur
+ // if the event is canceled by moving off the finger from the view.
+ // The text on alphabet keys are set at
+ // {@link #startEmojiPalettes(String,int,float,Typeface)}.
+ mAlphabetKeyLeft = (TextView)findViewById(R.id.emoji_keyboard_alphabet_left);
+ mAlphabetKeyLeft.setBackgroundResource(mEmojiFunctionalKeyBackgroundId);
+ mAlphabetKeyLeft.setTag(Constants.CODE_ALPHA_FROM_EMOJI);
+ mAlphabetKeyLeft.setOnTouchListener(this);
+ mAlphabetKeyLeft.setOnClickListener(this);
+ mAlphabetKeyRight = (TextView)findViewById(R.id.emoji_keyboard_alphabet_right);
+ mAlphabetKeyRight.setBackgroundResource(mEmojiFunctionalKeyBackgroundId);
+ mAlphabetKeyRight.setTag(Constants.CODE_ALPHA_FROM_EMOJI);
+ mAlphabetKeyRight.setOnTouchListener(this);
+ mAlphabetKeyRight.setOnClickListener(this);
final ImageView spaceKey = (ImageView)findViewById(R.id.emoji_keyboard_space);
spaceKey.setBackgroundResource(mKeyBackgroundId);
spaceKey.setTag(Constants.CODE_SPACE);
@@ -627,10 +634,20 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
// TODO:
}
- public void startEmojiPalettes() {
+ // Hack: These parameters are hacky.
+ public void startEmojiPalettes(final String switchToAlphaLabel, final int switchToAlphaColor,
+ final float switchToAlphaSize, final Typeface switchToAlphaTypeface) {
if (DEBUG_PAGER) {
Log.d(TAG, "allocate emoji palettes memory " + mCurrentPagerPosition);
}
+ mAlphabetKeyLeft.setText(switchToAlphaLabel);
+ mAlphabetKeyLeft.setTextColor(switchToAlphaColor);
+ mAlphabetKeyLeft.setTextSize(TypedValue.COMPLEX_UNIT_PX, switchToAlphaSize);
+ mAlphabetKeyLeft.setTypeface(switchToAlphaTypeface);
+ mAlphabetKeyRight.setText(switchToAlphaLabel);
+ mAlphabetKeyRight.setTextColor(switchToAlphaColor);
+ mAlphabetKeyRight.setTextSize(TypedValue.COMPLEX_UNIT_PX, switchToAlphaSize);
+ mAlphabetKeyRight.setTypeface(switchToAlphaTypeface);
mEmojiPager.setAdapter(mEmojiPalettesAdapter);
mEmojiPager.setCurrentItem(mCurrentPagerPosition);
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
index e5b814faf..cde5091c4 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
@@ -20,7 +20,6 @@ import static com.android.inputmethod.latin.Constants.ImeOption.FORCE_ASCII;
import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE;
import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE_COMPAT;
import static com.android.inputmethod.latin.Constants.ImeOption.NO_SETTINGS_KEY;
-import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.ASCII_CAPABLE;
import android.content.Context;
import android.content.res.Resources;
@@ -34,6 +33,7 @@ import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.compat.EditorInfoCompatUtils;
+import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
import com.android.inputmethod.keyboard.internal.KeyboardParams;
import com.android.inputmethod.keyboard.internal.KeysCache;
@@ -248,7 +248,7 @@ public final class KeyboardLayoutSet {
}
public Builder setSubtype(final InputMethodSubtype subtype) {
- final boolean asciiCapable = subtype.containsExtraValueKey(ASCII_CAPABLE);
+ final boolean asciiCapable = InputMethodSubtypeCompatUtils.isAsciiCapable(subtype);
@SuppressWarnings("deprecation")
final boolean deprecatedForceAscii = InputAttributes.inPrivateImeOptions(
mPackageName, FORCE_ASCII, mParams.mEditorInfo);
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index aebba60fb..6215e2710 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -19,6 +19,7 @@ package com.android.inputmethod.keyboard;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
+import android.graphics.Paint;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.ContextThemeWrapper;
@@ -30,6 +31,7 @@ import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
import com.android.inputmethod.compat.InputMethodServiceCompatUtils;
import com.android.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException;
import com.android.inputmethod.keyboard.internal.KeyboardState;
+import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.InputView;
import com.android.inputmethod.latin.LatinIME;
import com.android.inputmethod.latin.LatinImeLogger;
@@ -74,13 +76,13 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
private MainKeyboardView mKeyboardView;
private EmojiPalettesView mEmojiPalettesView;
private LatinIME mLatinIME;
- private Resources mResources;
private boolean mIsHardwareAcceleratedDrawingEnabled;
private KeyboardState mState;
private KeyboardLayoutSet mKeyboardLayoutSet;
private SettingsValues mCurrentSettingsValues;
+ private Key mSwitchToAlphaKey;
/** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of
* what user actually typed. */
@@ -106,7 +108,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
private void initInternal(final LatinIME latinIme, final SharedPreferences prefs) {
mLatinIME = latinIme;
- mResources = latinIme.getResources();
mPrefs = prefs;
mSubtypeSwitcher = SubtypeSwitcher.getInstance();
mState = new KeyboardState(this);
@@ -162,6 +163,8 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
mCurrentSettingsValues = settingsValues;
try {
mState.onLoadKeyboard();
+ final Keyboard symbols = mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS);
+ mSwitchToAlphaKey = symbols.getKey(Constants.CODE_SWITCH_ALPHA_SYMBOL);
} catch (KeyboardLayoutSetException e) {
Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause());
LatinImeLogger.logOnException(e.mKeyboardId.toString(), e.getCause());
@@ -287,7 +290,10 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
@Override
public void setEmojiKeyboard() {
mMainKeyboardFrame.setVisibility(View.GONE);
- mEmojiPalettesView.startEmojiPalettes();
+ final Paint paint = mKeyboardView.newLabelPaint(mSwitchToAlphaKey);
+ mEmojiPalettesView.startEmojiPalettes(
+ mSwitchToAlphaKey.getLabel(), paint.getColor(), paint.getTextSize(),
+ paint.getTypeface());
mEmojiPalettesView.setVisibility(View.VISIBLE);
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 422bd12a3..dd3ab9cce 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -582,6 +582,7 @@ public class KeyboardView extends View {
paint.setTypeface(mKeyDrawParams.mTypeface);
paint.setTextSize(mKeyDrawParams.mLabelSize);
} else {
+ paint.setColor(key.selectTextColor(mKeyDrawParams));
paint.setTypeface(key.selectTypeface(mKeyDrawParams));
paint.setTextSize(key.selectTextSize(mKeyDrawParams));
}
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index bd955ae6a..6c56b8aab 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -726,14 +726,6 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
}
@Override
- public boolean dispatchTouchEvent(MotionEvent event) {
- if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
- return AccessibleKeyboardViewProxy.getInstance().dispatchTouchEvent(event);
- }
- return super.dispatchTouchEvent(event);
- }
-
- @Override
public boolean onTouchEvent(final MotionEvent me) {
if (getKeyboard() == null) {
return false;
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index b4382bc2c..e428b1d54 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -142,7 +142,7 @@ public final class BinaryDictionaryFileDumper {
final ContentProviderClient client = context.getContentResolver().
acquireContentProviderClient(getProviderUriBuilder("").build());
if (null == client) return Collections.<WordListInfo>emptyList();
-
+ Cursor cursor = null;
try {
final Uri.Builder builder = getContentUriBuilderForType(clientId, client,
QUERY_PATH_DICT_INFO, locale.toString());
@@ -154,24 +154,22 @@ public final class BinaryDictionaryFileDumper {
final boolean isProtocolV2 = (QUERY_PARAMETER_PROTOCOL_VALUE.equals(
queryUri.getQueryParameter(QUERY_PARAMETER_PROTOCOL)));
- Cursor c = client.query(queryUri, DICTIONARY_PROJECTION, null, null, null);
- if (isProtocolV2 && null == c) {
+ cursor = client.query(queryUri, DICTIONARY_PROJECTION, null, null, null);
+ if (isProtocolV2 && null == cursor) {
reinitializeClientRecordInDictionaryContentProvider(context, client, clientId);
- c = client.query(queryUri, DICTIONARY_PROJECTION, null, null, null);
+ cursor = client.query(queryUri, DICTIONARY_PROJECTION, null, null, null);
}
- if (null == c) return Collections.<WordListInfo>emptyList();
- if (c.getCount() <= 0 || !c.moveToFirst()) {
- c.close();
+ if (null == cursor) return Collections.<WordListInfo>emptyList();
+ if (cursor.getCount() <= 0 || !cursor.moveToFirst()) {
return Collections.<WordListInfo>emptyList();
}
final ArrayList<WordListInfo> list = CollectionUtils.newArrayList();
do {
- final String wordListId = c.getString(0);
- final String wordListLocale = c.getString(1);
+ final String wordListId = cursor.getString(0);
+ final String wordListLocale = cursor.getString(1);
if (TextUtils.isEmpty(wordListId)) continue;
list.add(new WordListInfo(wordListId, wordListLocale));
- } while (c.moveToNext());
- c.close();
+ } while (cursor.moveToNext());
return list;
} catch (RemoteException e) {
// The documentation is unclear as to in which cases this may happen, but it probably
@@ -186,6 +184,9 @@ public final class BinaryDictionaryFileDumper {
Log.e(TAG, "Unexpected exception communicating with the dictionary pack", e);
return Collections.<WordListInfo>emptyList();
} finally {
+ if (null != cursor) {
+ cursor.close();
+ }
client.release();
}
}
diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
index d626ff926..11a9d1fe4 100644
--- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
@@ -139,23 +139,24 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
}
private void loadDictionaryAsyncForUri(final Uri uri) {
+ Cursor cursor = null;
try {
- Cursor cursor = mContext.getContentResolver()
- .query(uri, PROJECTION, null, null, null);
- if (cursor != null) {
- try {
- if (cursor.moveToFirst()) {
- sContactCountAtLastRebuild = getContactCount();
- addWords(cursor);
- }
- } finally {
- cursor.close();
- }
+ cursor = mContext.getContentResolver().query(uri, PROJECTION, null, null, null);
+ if (null == cursor) {
+ return;
+ }
+ if (cursor.moveToFirst()) {
+ sContactCountAtLastRebuild = getContactCount();
+ addWords(cursor);
}
} catch (final SQLiteException e) {
Log.e(TAG, "SQLiteException in the remote Contacts process.", e);
} catch (final IllegalStateException e) {
Log.e(TAG, "Contacts DB is having problems", e);
+ } finally {
+ if (null != cursor) {
+ cursor.close();
+ }
}
}
@@ -186,18 +187,20 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
private int getContactCount() {
// TODO: consider switching to a rawQuery("select count(*)...") on the database if
// performance is a bottleneck.
+ Cursor cursor = null;
try {
- final Cursor cursor = mContext.getContentResolver().query(
- Contacts.CONTENT_URI, PROJECTION_ID_ONLY, null, null, null);
- if (cursor != null) {
- try {
- return cursor.getCount();
- } finally {
- cursor.close();
- }
+ cursor = mContext.getContentResolver().query(Contacts.CONTENT_URI, PROJECTION_ID_ONLY,
+ null, null, null);
+ if (null == cursor) {
+ return 0;
}
+ return cursor.getCount();
} catch (final SQLiteException e) {
Log.e(TAG, "SQLiteException in the remote Contacts process.", e);
+ } finally {
+ if (null != cursor) {
+ cursor.close();
+ }
}
return 0;
}
@@ -281,26 +284,27 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
// Check all contacts since it's not possible to find out which names have changed.
// This is needed because it's possible to receive extraneous onChange events even when no
// name has changed.
- Cursor cursor = mContext.getContentResolver().query(
- Contacts.CONTENT_URI, PROJECTION, null, null, null);
- if (cursor != null) {
- try {
- if (cursor.moveToFirst()) {
- while (!cursor.isAfterLast()) {
- String name = cursor.getString(INDEX_NAME);
- if (isValidName(name) && !isNameInDictionary(name)) {
- if (DEBUG) {
- Log.d(TAG, "Contact name missing: " + name + " (runtime = "
- + (SystemClock.uptimeMillis() - startTime) + " ms)");
- }
- return true;
+ final Cursor cursor = mContext.getContentResolver().query(Contacts.CONTENT_URI, PROJECTION,
+ null, null, null);
+ if (null == cursor) {
+ return false;
+ }
+ try {
+ if (cursor.moveToFirst()) {
+ while (!cursor.isAfterLast()) {
+ String name = cursor.getString(INDEX_NAME);
+ if (isValidName(name) && !isNameInDictionary(name)) {
+ if (DEBUG) {
+ Log.d(TAG, "Contact name missing: " + name + " (runtime = "
+ + (SystemClock.uptimeMillis() - startTime) + " ms)");
}
- cursor.moveToNext();
+ return true;
}
+ cursor.moveToNext();
}
- } finally {
- cursor.close();
}
+ } finally {
+ cursor.close();
}
if (DEBUG) {
Log.d(TAG, "No contacts changed. (runtime = " + (SystemClock.uptimeMillis() - startTime)
diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java
index b01bc4ba5..726b3d141 100644
--- a/java/src/com/android/inputmethod/latin/InputAttributes.java
+++ b/java/src/com/android/inputmethod/latin/InputAttributes.java
@@ -20,9 +20,13 @@ import android.text.InputType;
import android.util.Log;
import android.view.inputmethod.EditorInfo;
+import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.InputTypeUtils;
import com.android.inputmethod.latin.utils.StringUtils;
+import java.util.ArrayList;
+import java.util.Arrays;
+
/**
* Class to hold attributes of the input field.
*/
@@ -31,6 +35,7 @@ public final class InputAttributes {
final public String mTargetApplicationPackageName;
final public boolean mInputTypeNoAutoCorrect;
+ final public boolean mIsPasswordField;
final public boolean mIsSettingsSuggestionStripOn;
final public boolean mApplicationSpecifiedCompletionOn;
final public boolean mShouldInsertSpacesAutomatically;
@@ -56,52 +61,48 @@ public final class InputAttributes {
Log.w(TAG, String.format("Unexpected input class: inputType=0x%08x"
+ " imeOptions=0x%08x", inputType, editorInfo.imeOptions));
}
+ mIsPasswordField = false;
mIsSettingsSuggestionStripOn = false;
mInputTypeNoAutoCorrect = false;
mApplicationSpecifiedCompletionOn = false;
mShouldInsertSpacesAutomatically = false;
- } else {
- final int variation = inputType & InputType.TYPE_MASK_VARIATION;
- final boolean flagNoSuggestions =
- 0 != (inputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
- final boolean flagMultiLine =
- 0 != (inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE);
- final boolean flagAutoCorrect =
- 0 != (inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT);
- final boolean flagAutoComplete =
- 0 != (inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
-
- // TODO: Have a helper method in InputTypeUtils
- // Make sure that passwords are not displayed in {@link SuggestionStripView}.
- if (InputTypeUtils.isPasswordInputType(inputType)
- || InputTypeUtils.isVisiblePasswordInputType(inputType)
- || InputTypeUtils.isEmailVariation(variation)
- || InputType.TYPE_TEXT_VARIATION_URI == variation
- || InputType.TYPE_TEXT_VARIATION_FILTER == variation
- || flagNoSuggestions
- || flagAutoComplete) {
- mIsSettingsSuggestionStripOn = false;
- } else {
- mIsSettingsSuggestionStripOn = true;
- }
+ return;
+ }
+ // inputClass == InputType.TYPE_CLASS_TEXT
+ final int variation = inputType & InputType.TYPE_MASK_VARIATION;
+ final boolean flagNoSuggestions =
+ 0 != (inputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
+ final boolean flagMultiLine =
+ 0 != (inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE);
+ final boolean flagAutoCorrect =
+ 0 != (inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT);
+ final boolean flagAutoComplete =
+ 0 != (inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
- mShouldInsertSpacesAutomatically = InputTypeUtils.isAutoSpaceFriendlyType(inputType);
-
- // If it's a browser edit field and auto correct is not ON explicitly, then
- // disable auto correction, but keep suggestions on.
- // If NO_SUGGESTIONS is set, don't do prediction.
- // If it's not multiline and the autoCorrect flag is not set, then don't correct
- if ((variation == InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT
- && !flagAutoCorrect)
- || flagNoSuggestions
- || (!flagAutoCorrect && !flagMultiLine)) {
- mInputTypeNoAutoCorrect = true;
- } else {
- mInputTypeNoAutoCorrect = false;
- }
+ mIsPasswordField = InputTypeUtils.isPasswordInputType(inputType)
+ || InputTypeUtils.isVisiblePasswordInputType(inputType);
+ // TODO: Have a helper method in InputTypeUtils
+ // Make sure that passwords are not displayed in {@link SuggestionStripView}.
+ final boolean noSuggestionStrip = mIsPasswordField
+ || InputTypeUtils.isEmailVariation(variation)
+ || InputType.TYPE_TEXT_VARIATION_URI == variation
+ || InputType.TYPE_TEXT_VARIATION_FILTER == variation
+ || flagNoSuggestions
+ || flagAutoComplete;
+ mIsSettingsSuggestionStripOn = !noSuggestionStrip;
- mApplicationSpecifiedCompletionOn = flagAutoComplete && isFullscreenMode;
- }
+ mShouldInsertSpacesAutomatically = InputTypeUtils.isAutoSpaceFriendlyType(inputType);
+
+ // If it's a browser edit field and auto correct is not ON explicitly, then
+ // disable auto correction, but keep suggestions on.
+ // If NO_SUGGESTIONS is set, don't do prediction.
+ // If it's not multiline and the autoCorrect flag is not set, then don't correct
+ mInputTypeNoAutoCorrect =
+ (variation == InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT && !flagAutoCorrect)
+ || flagNoSuggestions
+ || (!flagAutoCorrect && !flagMultiLine);
+
+ mApplicationSpecifiedCompletionOn = flagAutoComplete && isFullscreenMode;
}
public boolean isTypeNull() {
@@ -114,96 +115,142 @@ public final class InputAttributes {
@SuppressWarnings("unused")
private void dumpFlags(final int inputType) {
- Log.i(TAG, "Input class:");
final int inputClass = inputType & InputType.TYPE_MASK_CLASS;
- if (inputClass == InputType.TYPE_CLASS_TEXT)
- Log.i(TAG, " TYPE_CLASS_TEXT");
- if (inputClass == InputType.TYPE_CLASS_PHONE)
- Log.i(TAG, " TYPE_CLASS_PHONE");
- if (inputClass == InputType.TYPE_CLASS_NUMBER)
- Log.i(TAG, " TYPE_CLASS_NUMBER");
- if (inputClass == InputType.TYPE_CLASS_DATETIME)
- Log.i(TAG, " TYPE_CLASS_DATETIME");
- Log.i(TAG, "Variation:");
- switch (InputType.TYPE_MASK_VARIATION & inputType) {
- case InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS:
- Log.i(TAG, " TYPE_TEXT_VARIATION_EMAIL_ADDRESS");
- break;
- case InputType.TYPE_TEXT_VARIATION_EMAIL_SUBJECT:
- Log.i(TAG, " TYPE_TEXT_VARIATION_EMAIL_SUBJECT");
- break;
- case InputType.TYPE_TEXT_VARIATION_FILTER:
- Log.i(TAG, " TYPE_TEXT_VARIATION_FILTER");
- break;
- case InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE:
- Log.i(TAG, " TYPE_TEXT_VARIATION_LONG_MESSAGE");
- break;
- case InputType.TYPE_TEXT_VARIATION_NORMAL:
- Log.i(TAG, " TYPE_TEXT_VARIATION_NORMAL");
- break;
- case InputType.TYPE_TEXT_VARIATION_PASSWORD:
- Log.i(TAG, " TYPE_TEXT_VARIATION_PASSWORD");
- break;
- case InputType.TYPE_TEXT_VARIATION_PERSON_NAME:
- Log.i(TAG, " TYPE_TEXT_VARIATION_PERSON_NAME");
- break;
- case InputType.TYPE_TEXT_VARIATION_PHONETIC:
- Log.i(TAG, " TYPE_TEXT_VARIATION_PHONETIC");
- break;
- case InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS:
- Log.i(TAG, " TYPE_TEXT_VARIATION_POSTAL_ADDRESS");
- break;
- case InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE:
- Log.i(TAG, " TYPE_TEXT_VARIATION_SHORT_MESSAGE");
- break;
- case InputType.TYPE_TEXT_VARIATION_URI:
- Log.i(TAG, " TYPE_TEXT_VARIATION_URI");
- break;
- case InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD:
- Log.i(TAG, " TYPE_TEXT_VARIATION_VISIBLE_PASSWORD");
- break;
- case InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT:
- Log.i(TAG, " TYPE_TEXT_VARIATION_WEB_EDIT_TEXT");
- break;
- case InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS:
- Log.i(TAG, " TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS");
- break;
- case InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD:
- Log.i(TAG, " TYPE_TEXT_VARIATION_WEB_PASSWORD");
- break;
- default:
- Log.i(TAG, " Unknown variation");
- break;
+ final String inputClassString = toInputClassString(inputClass);
+ final String variationString = toVariationString(
+ inputClass, inputType & InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
+ final String flagsString = toFlagsString(inputType & InputType.TYPE_MASK_FLAGS);
+ Log.i(TAG, "Input class: " + inputClassString);
+ Log.i(TAG, "Variation: " + variationString);
+ Log.i(TAG, "Flags: " + flagsString);
+ }
+
+ private static String toInputClassString(final int inputClass) {
+ switch (inputClass) {
+ case InputType.TYPE_CLASS_TEXT:
+ return "TYPE_CLASS_TEXT";
+ case InputType.TYPE_CLASS_PHONE:
+ return "TYPE_CLASS_PHONE";
+ case InputType.TYPE_CLASS_NUMBER:
+ return "TYPE_CLASS_NUMBER";
+ case InputType.TYPE_CLASS_DATETIME:
+ return "TYPE_CLASS_DATETIME";
+ default:
+ return String.format("unknownInputClass<0x%08x>", inputClass);
+ }
+ }
+
+ private static String toVariationString(final int inputClass, final int variation) {
+ switch (inputClass) {
+ case InputType.TYPE_CLASS_TEXT:
+ return toTextVariationString(variation);
+ case InputType.TYPE_CLASS_NUMBER:
+ return toNumberVariationString(variation);
+ case InputType.TYPE_CLASS_DATETIME:
+ return toDatetimeVariationString(variation);
+ default:
+ return "";
}
- Log.i(TAG, "Flags:");
- if (0 != (inputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS))
- Log.i(TAG, " TYPE_TEXT_FLAG_NO_SUGGESTIONS");
- if (0 != (inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE))
- Log.i(TAG, " TYPE_TEXT_FLAG_MULTI_LINE");
- if (0 != (inputType & InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE))
- Log.i(TAG, " TYPE_TEXT_FLAG_IME_MULTI_LINE");
- if (0 != (inputType & InputType.TYPE_TEXT_FLAG_CAP_WORDS))
- Log.i(TAG, " TYPE_TEXT_FLAG_CAP_WORDS");
- if (0 != (inputType & InputType.TYPE_TEXT_FLAG_CAP_SENTENCES))
- Log.i(TAG, " TYPE_TEXT_FLAG_CAP_SENTENCES");
- if (0 != (inputType & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS))
- Log.i(TAG, " TYPE_TEXT_FLAG_CAP_CHARACTERS");
- if (0 != (inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT))
- Log.i(TAG, " TYPE_TEXT_FLAG_AUTO_CORRECT");
- if (0 != (inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE))
- Log.i(TAG, " TYPE_TEXT_FLAG_AUTO_COMPLETE");
+ }
+
+ private static String toTextVariationString(final int variation) {
+ switch (variation) {
+ case InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS:
+ return " TYPE_TEXT_VARIATION_EMAIL_ADDRESS";
+ case InputType.TYPE_TEXT_VARIATION_EMAIL_SUBJECT:
+ return "TYPE_TEXT_VARIATION_EMAIL_SUBJECT";
+ case InputType.TYPE_TEXT_VARIATION_FILTER:
+ return "TYPE_TEXT_VARIATION_FILTER";
+ case InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE:
+ return "TYPE_TEXT_VARIATION_LONG_MESSAGE";
+ case InputType.TYPE_TEXT_VARIATION_NORMAL:
+ return "TYPE_TEXT_VARIATION_NORMAL";
+ case InputType.TYPE_TEXT_VARIATION_PASSWORD:
+ return "TYPE_TEXT_VARIATION_PASSWORD";
+ case InputType.TYPE_TEXT_VARIATION_PERSON_NAME:
+ return "TYPE_TEXT_VARIATION_PERSON_NAME";
+ case InputType.TYPE_TEXT_VARIATION_PHONETIC:
+ return "TYPE_TEXT_VARIATION_PHONETIC";
+ case InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS:
+ return "TYPE_TEXT_VARIATION_POSTAL_ADDRESS";
+ case InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE:
+ return "TYPE_TEXT_VARIATION_SHORT_MESSAGE";
+ case InputType.TYPE_TEXT_VARIATION_URI:
+ return "TYPE_TEXT_VARIATION_URI";
+ case InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD:
+ return "TYPE_TEXT_VARIATION_VISIBLE_PASSWORD";
+ case InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT:
+ return "TYPE_TEXT_VARIATION_WEB_EDIT_TEXT";
+ case InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS:
+ return "TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS";
+ case InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD:
+ return "TYPE_TEXT_VARIATION_WEB_PASSWORD";
+ default:
+ return String.format("unknownVariation<0x%08x>", variation);
+ }
+ }
+
+ private static String toNumberVariationString(final int variation) {
+ switch (variation) {
+ case InputType.TYPE_NUMBER_VARIATION_NORMAL:
+ return "TYPE_NUMBER_VARIATION_NORMAL";
+ case InputType.TYPE_NUMBER_VARIATION_PASSWORD:
+ return "TYPE_NUMBER_VARIATION_PASSWORD";
+ default:
+ return String.format("unknownVariation<0x%08x>", variation);
+ }
+ }
+
+ private static String toDatetimeVariationString(final int variation) {
+ switch (variation) {
+ case InputType.TYPE_DATETIME_VARIATION_NORMAL:
+ return "TYPE_DATETIME_VARIATION_NORMAL";
+ case InputType.TYPE_DATETIME_VARIATION_DATE:
+ return "TYPE_DATETIME_VARIATION_DATE";
+ case InputType.TYPE_DATETIME_VARIATION_TIME:
+ return "TYPE_DATETIME_VARIATION_TIME";
+ default:
+ return String.format("unknownVariation<0x%08x>", variation);
+ }
+ }
+
+ private static String toFlagsString(final int flags) {
+ final ArrayList<String> flagsArray = CollectionUtils.newArrayList();
+ if (0 != (flags & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS))
+ flagsArray.add("TYPE_TEXT_FLAG_NO_SUGGESTIONS");
+ if (0 != (flags & InputType.TYPE_TEXT_FLAG_MULTI_LINE))
+ flagsArray.add("TYPE_TEXT_FLAG_MULTI_LINE");
+ if (0 != (flags & InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE))
+ flagsArray.add("TYPE_TEXT_FLAG_IME_MULTI_LINE");
+ if (0 != (flags & InputType.TYPE_TEXT_FLAG_CAP_WORDS))
+ flagsArray.add("TYPE_TEXT_FLAG_CAP_WORDS");
+ if (0 != (flags & InputType.TYPE_TEXT_FLAG_CAP_SENTENCES))
+ flagsArray.add("TYPE_TEXT_FLAG_CAP_SENTENCES");
+ if (0 != (flags & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS))
+ flagsArray.add("TYPE_TEXT_FLAG_CAP_CHARACTERS");
+ if (0 != (flags & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT))
+ flagsArray.add("TYPE_TEXT_FLAG_AUTO_CORRECT");
+ if (0 != (flags & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE))
+ flagsArray.add("TYPE_TEXT_FLAG_AUTO_COMPLETE");
+ return flagsArray.isEmpty() ? "" : Arrays.toString(flagsArray.toArray());
}
// Pretty print
@Override
public String toString() {
- return "\n mInputTypeNoAutoCorrect = " + mInputTypeNoAutoCorrect
- + "\n mIsSettingsSuggestionStripOn = " + mIsSettingsSuggestionStripOn
- + "\n mApplicationSpecifiedCompletionOn = " + mApplicationSpecifiedCompletionOn;
+ return String.format(
+ "%s: inputType=0x%08x%s%s%s%s%s targetApp=%s\n", getClass().getSimpleName(),
+ mInputType,
+ (mInputTypeNoAutoCorrect ? " noAutoCorrect" : ""),
+ (mIsPasswordField ? " password" : ""),
+ (mIsSettingsSuggestionStripOn ? " suggestionStrip" : ""),
+ (mApplicationSpecifiedCompletionOn ? " appSpecified" : ""),
+ (mShouldInsertSpacesAutomatically ? " insertSpaces" : ""),
+ mTargetApplicationPackageName);
}
- public static boolean inPrivateImeOptions(String packageName, String key,
- EditorInfo editorInfo) {
+ public static boolean inPrivateImeOptions(final String packageName, final String key,
+ final EditorInfo editorInfo) {
if (editorInfo == null) return false;
final String findingKey = (packageName != null) ? packageName + "." + key : key;
return StringUtils.containsInCommaSplittableText(findingKey, editorInfo.privateImeOptions);
diff --git a/java/src/com/android/inputmethod/latin/InputView.java b/java/src/com/android/inputmethod/latin/InputView.java
index 76b0912f6..ea7859e60 100644
--- a/java/src/com/android/inputmethod/latin/InputView.java
+++ b/java/src/com/android/inputmethod/latin/InputView.java
@@ -31,6 +31,7 @@ public final class InputView extends LinearLayout {
private final Rect mInputViewRect = new Rect();
private KeyboardTopPaddingForwarder mKeyboardTopPaddingForwarder;
private MoreSuggestionsViewCanceler mMoreSuggestionsViewCanceler;
+ private MotionEventForwarder<?, ?> mActiveForwarder;
public InputView(final Context context, final AttributeSet attrs) {
super(context, attrs, 0);
@@ -53,42 +54,62 @@ public final class InputView extends LinearLayout {
}
@Override
- public boolean dispatchTouchEvent(final MotionEvent me) {
+ public boolean onInterceptTouchEvent(final MotionEvent me) {
final Rect rect = mInputViewRect;
getGlobalVisibleRect(rect);
- final int x = (int)me.getX() + rect.left;
- final int y = (int)me.getY() + rect.top;
-
- // The touch events that hit the top padding of keyboard should be
- // forwarded to {@link SuggestionStripView}.
- if (mKeyboardTopPaddingForwarder.dispatchTouchEvent(x, y, me)) {
+ final int index = me.getActionIndex();
+ final int x = (int)me.getX(index) + rect.left;
+ final int y = (int)me.getY(index) + rect.top;
+
+ // The touch events that hit the top padding of keyboard should be forwarded to
+ // {@link SuggestionStripView}.
+ if (mKeyboardTopPaddingForwarder.onInterceptTouchEvent(x, y, me)) {
+ mActiveForwarder = mKeyboardTopPaddingForwarder;
return true;
}
+
// To cancel {@link MoreSuggestionsView}, we should intercept a touch event to
// {@link MainKeyboardView} and dismiss the {@link MoreSuggestionsView}.
- if (mMoreSuggestionsViewCanceler.dispatchTouchEvent(x, y, me)) {
+ if (mMoreSuggestionsViewCanceler.onInterceptTouchEvent(x, y, me)) {
+ mActiveForwarder = mMoreSuggestionsViewCanceler;
return true;
}
- return super.dispatchTouchEvent(me);
+
+ mActiveForwarder = null;
+ return false;
+ }
+
+ @Override
+ public boolean onTouchEvent(final MotionEvent me) {
+ if (mActiveForwarder == null) {
+ return super.onTouchEvent(me);
+ }
+
+ final Rect rect = mInputViewRect;
+ getGlobalVisibleRect(rect);
+ final int index = me.getActionIndex();
+ final int x = (int)me.getX(index) + rect.left;
+ final int y = (int)me.getY(index) + rect.top;
+ return mActiveForwarder.onTouchEvent(x, y, me);
}
/**
- * This class forwards series of {@link MotionEvent}s from <code>Forwarder</code> view to
- * <code>Receiver</code> view.
+ * This class forwards series of {@link MotionEvent}s from <code>SenderView</code> to
+ * <code>ReceiverView</code>.
*
- * @param <Sender> a {@link View} that may send a {@link MotionEvent} to <Receiver>.
- * @param <Receiver> a {@link View} that receives forwarded {@link MotionEvent} from
- * <Forwarder>.
+ * @param <SenderView> a {@link View} that may send a {@link MotionEvent} to <ReceiverView>.
+ * @param <ReceiverView> a {@link View} that receives forwarded {@link MotionEvent} from
+ * <SenderView>.
*/
- private static abstract class MotionEventForwarder<Sender extends View, Receiver extends View> {
- protected final Sender mSenderView;
- protected final Receiver mReceiverView;
+ private static abstract class
+ MotionEventForwarder<SenderView extends View, ReceiverView extends View> {
+ protected final SenderView mSenderView;
+ protected final ReceiverView mReceiverView;
- private boolean mIsForwardingEvent;
protected final Rect mEventSendingRect = new Rect();
protected final Rect mEventReceivingRect = new Rect();
- public MotionEventForwarder(final Sender senderView, final Receiver receiverView) {
+ public MotionEventForwarder(final SenderView senderView, final ReceiverView receiverView) {
mSenderView = senderView;
mReceiverView = receiverView;
}
@@ -96,12 +117,12 @@ public final class InputView extends LinearLayout {
// Return true if a touch event of global coordinate x, y needs to be forwarded.
protected abstract boolean needsToForward(final int x, final int y);
- // Translate global x-coordinate to <code>Receiver</code> local coordinate.
+ // Translate global x-coordinate to <code>ReceiverView</code> local coordinate.
protected int translateX(final int x) {
return x - mEventReceivingRect.left;
}
- // Translate global y-coordinate to <code>Receiver</code> local coordinate.
+ // Translate global y-coordinate to <code>ReceiverView</code> local coordinate.
protected int translateY(final int y) {
return y - mEventReceivingRect.top;
}
@@ -109,49 +130,36 @@ public final class InputView extends LinearLayout {
// Callback when a {@link MotionEvent} is forwarded.
protected void onForwardingEvent(final MotionEvent me) {}
- // Dispatches a {@link MotioneEvent} to <code>Receiver</code> if needed and returns true.
- // Otherwise returns false.
- public boolean dispatchTouchEvent(final int x, final int y, final MotionEvent me) {
- // Forwards a {link MotionEvent} only if both <code>Sender</code> and
- // <code>Receiver</code> are visible.
+ // Returns true if a {@link MotionEvent} is needed to be forwarded to
+ // <code>ReceiverView</code>. Otherwise returns false.
+ public boolean onInterceptTouchEvent(final int x, final int y, final MotionEvent me) {
+ // Forwards a {link MotionEvent} only if both <code>SenderView</code> and
+ // <code>ReceiverView</code> are visible.
if (mSenderView.getVisibility() != View.VISIBLE ||
mReceiverView.getVisibility() != View.VISIBLE) {
return false;
}
- final Rect sendingRect = mEventSendingRect;
- mSenderView.getGlobalVisibleRect(sendingRect);
- if (!mIsForwardingEvent && !sendingRect.contains(x, y)) {
+ mSenderView.getGlobalVisibleRect(mEventSendingRect);
+ if (!mEventSendingRect.contains(x, y)) {
return false;
}
- boolean shouldForwardToReceiver = false;
-
- switch (me.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- // If the down event happens in the forwarding area, successive {@link MotionEvent}s
- // should be forwarded.
+ if (me.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ // If the down event happens in the forwarding area, successive
+ // {@link MotionEvent}s should be forwarded to <code>ReceiverView</code>.
if (needsToForward(x, y)) {
- mIsForwardingEvent = true;
- shouldForwardToReceiver = true;
+ return true;
}
- break;
- case MotionEvent.ACTION_MOVE:
- shouldForwardToReceiver = mIsForwardingEvent;
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- shouldForwardToReceiver = mIsForwardingEvent;
- mIsForwardingEvent = false;
- break;
}
- if (!shouldForwardToReceiver) {
- return false;
- }
+ return false;
+ }
- final Rect receivingRect = mEventReceivingRect;
- mReceiverView.getGlobalVisibleRect(receivingRect);
- // Translate global coordinates to <code>Receiver</code> local coordinates.
+ // Returns true if a {@link MotionEvent} is forwarded to <code>ReceiverView</code>.
+ // Otherwise returns false.
+ public boolean onTouchEvent(final int x, final int y, final MotionEvent me) {
+ mReceiverView.getGlobalVisibleRect(mEventReceivingRect);
+ // Translate global coordinates to <code>ReceiverView</code> local coordinates.
me.setLocation(translateX(x), translateY(y));
mReceiverView.dispatchTouchEvent(me);
onForwardingEvent(me);
@@ -189,8 +197,7 @@ public final class InputView extends LinearLayout {
protected int translateY(final int y) {
final int translatedY = super.translateY(y);
if (isInKeyboardTopPadding(y)) {
- // The forwarded event should have coordinates that are inside of
- // the target.
+ // The forwarded event should have coordinates that are inside of the target.
return Math.min(translatedY, mEventReceivingRect.height() - 1);
}
return translatedY;
@@ -200,8 +207,8 @@ public final class InputView extends LinearLayout {
/**
* This class forwards {@link MotionEvent}s happened in the {@link MainKeyboardView} to
* {@link SuggestionStripView} when the {@link MoreSuggestionsView} is showing.
- * {@link SuggestionStripView} dismisses {@link MoreSuggestionsView} when it receives those
- * events.
+ * {@link SuggestionStripView} dismisses {@link MoreSuggestionsView} when it receives any event
+ * outside of it.
*/
private static class MoreSuggestionsViewCanceler
extends MotionEventForwarder<MainKeyboardView, SuggestionStripView> {
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index d1125afcc..75ba24d75 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -171,10 +171,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
public void onCreate() {
final Resources res = getOwnerInstance().getResources();
- mDelayUpdateSuggestions =
- res.getInteger(R.integer.config_delay_update_suggestions);
- mDelayUpdateShiftState =
- res.getInteger(R.integer.config_delay_update_shift_state);
+ mDelayUpdateSuggestions = res.getInteger(R.integer.config_delay_update_suggestions);
+ mDelayUpdateShiftState = res.getInteger(R.integer.config_delay_update_shift_state);
mDoubleSpacePeriodTimeout =
res.getInteger(R.integer.config_double_space_period_timeout);
}
@@ -339,12 +337,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private void executePendingImsCallback(final LatinIME latinIme, final EditorInfo editorInfo,
boolean restarting) {
- if (mHasPendingFinishInputView)
+ if (mHasPendingFinishInputView) {
latinIme.onFinishInputViewInternal(mHasPendingFinishInput);
- if (mHasPendingFinishInput)
+ }
+ if (mHasPendingFinishInput) {
latinIme.onFinishInputInternal();
- if (mHasPendingStartInput)
+ }
+ if (mHasPendingStartInput) {
latinIme.onStartInputInternal(editorInfo, restarting);
+ }
resetPendingImsCallback();
}
@@ -579,9 +580,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
(mInputLogic.mSuggest == null) ? null : mInputLogic.mSuggest.mDictionaryFacilitator;
// Creates new dictionary facilitator for the new locale.
final DictionaryFacilitatorForSuggest dictionaryFacilitator =
- new DictionaryFacilitatorForSuggest(this /* context */, locale,
- settingsValues, this /* DictionaryInitializationListener */,
- oldDictionaryFacilitator);
+ new DictionaryFacilitatorForSuggest(this /* context */, locale, settingsValues,
+ this /* DictionaryInitializationListener */, oldDictionaryFacilitator);
final Suggest newSuggest = new Suggest(locale, dictionaryFacilitator);
if (settingsValues.mCorrectionEnabled) {
newSuggest.setAutoCorrectionThreshold(settingsValues.mAutoCorrectionThreshold);
@@ -658,7 +658,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
.findViewById(android.R.id.extractArea);
mKeyPreviewBackingView = view.findViewById(R.id.key_preview_backing);
mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view);
- if (mSuggestionStripView != null) {
+ if (hasSuggestionStripView()) {
mSuggestionStripView.setListener(this, view);
}
if (LatinImeLogger.sVISUALDEBUG) {
@@ -738,13 +738,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
ResearchLogger.latinIME_onStartInputViewInternal(editorInfo, prefs);
}
if (InputAttributes.inPrivateImeOptions(null, NO_MICROPHONE_COMPAT, editorInfo)) {
- Log.w(TAG, "Deprecated private IME option specified: "
- + editorInfo.privateImeOptions);
+ Log.w(TAG, "Deprecated private IME option specified: " + editorInfo.privateImeOptions);
Log.w(TAG, "Use " + getPackageName() + "." + NO_MICROPHONE + " instead");
}
if (InputAttributes.inPrivateImeOptions(getPackageName(), FORCE_ASCII, editorInfo)) {
- Log.w(TAG, "Deprecated private IME option specified: "
- + editorInfo.privateImeOptions);
+ Log.w(TAG, "Deprecated private IME option specified: " + editorInfo.privateImeOptions);
Log.w(TAG, "Use EditorInfo.IME_FLAG_FORCE_ASCII flag instead");
}
@@ -781,11 +779,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (null != suggest && null != currentLocale && !currentLocale.equals(suggest.mLocale)) {
initSuggest();
}
- if (mSuggestionStripView != null) {
- // This will set the punctuation suggestions if next word suggestion is off;
- // otherwise it will clear the suggestion strip.
- setNeutralSuggestionStrip();
- }
// Sometimes, while rotating, for some reason the framework tells the app we are not
// connected to it and that means we can't refresh the cache. In this case, schedule a
@@ -834,8 +827,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Space state must be updated before calling updateShiftState
switcher.updateShiftState();
}
- setSuggestionStripShownInternal(
- isSuggestionsStripVisible(), /* needsInputViewShown */ false);
+ // This will set the punctuation suggestions if next word suggestion is off;
+ // otherwise it will clear the suggestion strip.
+ setNeutralSuggestionStripInternal(false /* needsInputViewShown */);
mHandler.cancelUpdateSuggestionStrip();
mHandler.cancelDoubleSpacePeriodTimer();
@@ -894,12 +888,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
composingSpanStart, composingSpanEnd);
if (DEBUG) {
- Log.i(TAG, "onUpdateSelection: oss=" + oldSelStart
- + ", ose=" + oldSelEnd
- + ", nss=" + newSelStart
- + ", nse=" + newSelEnd
- + ", cs=" + composingSpanStart
- + ", ce=" + composingSpanEnd);
+ Log.i(TAG, "onUpdateSelection: oss=" + oldSelStart + ", ose=" + oldSelEnd
+ + ", nss=" + newSelStart + ", nse=" + newSelEnd
+ + ", cs=" + composingSpanStart + ", ce=" + composingSpanEnd);
}
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.latinIME_onUpdateSelection(oldSelStart, oldSelEnd,
@@ -983,7 +974,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
}
- if (!mSettings.getCurrent().isApplicationSpecifiedCompletionsOn()) return;
+ if (!mSettings.getCurrent().isApplicationSpecifiedCompletionsOn()) {
+ return;
+ }
if (applicationSpecifiedCompletions == null) {
setNeutralSuggestionStrip();
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
@@ -997,27 +990,25 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final ArrayList<SuggestedWords.SuggestedWordInfo> applicationSuggestedWords =
SuggestedWords.getFromApplicationSpecifiedCompletions(
applicationSpecifiedCompletions);
- final SuggestedWords suggestedWords = new SuggestedWords(
- applicationSuggestedWords, null /* rawSuggestions */,
- false /* typedWordValid */,
- false /* willAutoCorrect */,
- false /* isObsoleteSuggestions */,
- false /* isPrediction */);
- // When in fullscreen mode, show completions generated by the application
- setSuggestedWords(suggestedWords, true /* shouldShow */);
+ final SuggestedWords suggestedWords = new SuggestedWords(applicationSuggestedWords,
+ null /* rawSuggestions */, false /* typedWordValid */, false /* willAutoCorrect */,
+ false /* isObsoleteSuggestions */, false /* isPrediction */);
+ // When in fullscreen mode, show completions generated by the application forcibly
+ setSuggestedWords(suggestedWords, true /* isSuggestionStripVisible */,
+ true /* needsInputViewShown */);
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions);
}
}
- private void setSuggestionStripShownInternal(final boolean shown,
+ private void setSuggestionStripShownInternal(final boolean isSuggestionStripVisible,
final boolean needsInputViewShown) {
// TODO: Modify this if we support suggestions with hard keyboard
- if (!onEvaluateInputViewShown() || null == mSuggestionStripView) {
+ if (!onEvaluateInputViewShown() || !hasSuggestionStripView()) {
return;
}
final boolean inputViewShown = mKeyboardSwitcher.isShowingMainKeyboardOrEmojiPalettes();
- final boolean shouldShowSuggestions = shown
+ final boolean shouldShowSuggestions = isSuggestionStripVisible
&& (needsInputViewShown ? inputViewShown : true);
if (shouldShowSuggestions) {
mSuggestionStripView.setVisibility(View.VISIBLE);
@@ -1057,7 +1048,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
public void onComputeInsets(final InputMethodService.Insets outInsets) {
super.onComputeInsets(outInsets);
final View visibleKeyboardView = mKeyboardSwitcher.getVisibleKeyboardView();
- if (visibleKeyboardView == null || mSuggestionStripView == null) {
+ if (visibleKeyboardView == null || !hasSuggestionStripView()) {
return;
}
final int adjustedBackingHeight = getAdjustedBackingViewHeight();
@@ -1177,18 +1168,28 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
public void onClick(final DialogInterface di, final int position) {
di.dismiss();
ImportantNoticeUtils.updateLastImportantNoticeVersion(context);
- setNeutralSuggestionStrip();
+ if (position == DialogInterface.BUTTON_POSITIVE) {
+ setNeutralSuggestionStrip();
+ return;
+ }
+ if (position == DialogInterface.BUTTON_NEGATIVE) {
+ launchSettings();
+ return;
+ }
}
};
final AlertDialog.Builder builder =
new AlertDialog.Builder(context, AlertDialog.THEME_HOLO_DARK);
builder.setMessage(R.string.important_notice_contents)
- .setPositiveButton(android.R.string.ok, listener);
+ .setPositiveButton(android.R.string.ok, listener)
+ .setNegativeButton(R.string.go_to_settings, listener);
showOptionDialog(builder.create(), true /* cancelable */);
}
public void displaySettingsDialog() {
- if (isShowingOptionDialog()) return;
+ if (isShowingOptionDialog()) {
+ return;
+ }
showSubtypeSelectorAndSettings();
}
@@ -1248,8 +1249,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mSubtypeSwitcher.switchToShortcutIME(this);
// Still call the *#onCodeInput methods for readability.
}
- mInputLogic.onCodeInput(codeToSend, keyX, keyY, mSettings.getCurrent(),
- mHandler, mKeyboardSwitcher);
+ mInputLogic.onCodeInput(codeToSend, keyX, keyY, mSettings.getCurrent(), mHandler,
+ mKeyboardSwitcher);
mKeyboardSwitcher.onCodeInput(codePoint);
}
@@ -1306,27 +1307,28 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Nothing to do so far.
}
- // TODO: remove this, read this directly from mInputLogic or something in the tests
- @UsedForTesting
- public boolean isShowingPunctuationList() {
- return mInputLogic.isShowingPunctuationList(mSettings.getCurrent());
- }
-
// TODO[IL]: Define a clear interface for this
- public boolean isSuggestionsStripVisible() {
- final SettingsValues currentSettings = mSettings.getCurrent();
- if (mSuggestionStripView == null)
+ public boolean isSuggestionStripVisible() {
+ if (!hasSuggestionStripView()) {
return false;
- if (mSuggestionStripView.isShowingAddToDictionaryHint())
- return true;
- if (ImportantNoticeUtils.hasNewImportantNoticeAndNotInSetupWizard(this))
+ }
+ if (mSuggestionStripView.isShowingAddToDictionaryHint()) {
return true;
- if (null == currentSettings)
+ }
+ final SettingsValues currentSettings = mSettings.getCurrent();
+ if (null == currentSettings) {
return false;
- if (!currentSettings.isSuggestionStripVisible())
+ }
+ if (ImportantNoticeUtils.shouldShowImportantNotice(this,
+ currentSettings.mInputAttributes)) {
+ return true;
+ }
+ if (!currentSettings.isSuggestionStripVisible()) {
return false;
- if (currentSettings.isApplicationSpecifiedCompletionsOn())
+ }
+ if (currentSettings.isApplicationSpecifiedCompletionsOn()) {
return true;
+ }
return currentSettings.isSuggestionsRequested();
}
@@ -1342,30 +1344,35 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override
public void dismissAddToDictionaryHint() {
- if (null != mSuggestionStripView) {
- mSuggestionStripView.dismissAddToDictionaryHint();
+ if (!hasSuggestionStripView()) {
+ return;
}
+ mSuggestionStripView.dismissAddToDictionaryHint();
}
// TODO[IL]: Define a clear interface for this
- public void setSuggestedWords(final SuggestedWords suggestedWords, final boolean shouldShow) {
+ public void setSuggestedWords(final SuggestedWords suggestedWords,
+ final boolean isSuggestionStripVisible, final boolean needsInputViewShown) {
mInputLogic.setSuggestedWords(suggestedWords);
- if (mSuggestionStripView != null) {
- final boolean showSuggestions;
- if (SuggestedWords.EMPTY == suggestedWords
- || suggestedWords.isPunctuationSuggestions()
- || !mSettings.getCurrent().isSuggestionsRequested()) {
- showSuggestions = !mSuggestionStripView.maybeShowImportantNoticeTitle();
- } else {
- showSuggestions = true;
- }
- if (showSuggestions) {
- mSuggestionStripView.setSuggestions(suggestedWords,
- SubtypeLocaleUtils.isRtlLanguage(mSubtypeSwitcher.getCurrentSubtype()));
- }
- mKeyboardSwitcher.onAutoCorrectionStateChanged(suggestedWords.mWillAutoCorrect);
- setSuggestionStripShownInternal(shouldShow, true /* needsInputViewShown */);
+ if (!hasSuggestionStripView()) {
+ return;
+ }
+ final SettingsValues currentSettings = mSettings.getCurrent();
+ final boolean showSuggestions;
+ if (SuggestedWords.EMPTY == suggestedWords
+ || suggestedWords.isPunctuationSuggestions()
+ || !currentSettings.isSuggestionsRequested()) {
+ showSuggestions = !mSuggestionStripView.maybeShowImportantNoticeTitle(
+ currentSettings.mInputAttributes);
+ } else {
+ showSuggestions = true;
+ }
+ if (showSuggestions) {
+ mSuggestionStripView.setSuggestions(suggestedWords,
+ SubtypeLocaleUtils.isRtlLanguage(mSubtypeSwitcher.getCurrentSubtype()));
}
+ mKeyboardSwitcher.onAutoCorrectionStateChanged(suggestedWords.mWillAutoCorrect);
+ setSuggestionStripShownInternal(isSuggestionStripVisible, needsInputViewShown);
}
// TODO[IL]: Move this out of LatinIME.
@@ -1404,9 +1411,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
suggest.getSuggestedWords(mInputLogic.mWordComposer,
mInputLogic.mWordComposer.getPreviousWordForSuggestion(),
- keyboard.getProximityInfo(),
- currentSettings.mBlockPotentiallyOffensive, currentSettings.mCorrectionEnabled,
- additionalFeaturesOptions, sessionId, sequenceNumber, callback);
+ keyboard.getProximityInfo(), currentSettings.mBlockPotentiallyOffensive,
+ currentSettings.mCorrectionEnabled, additionalFeaturesOptions, sessionId,
+ sequenceNumber, callback);
}
// TODO[IL]: Move this to InputLogic
@@ -1420,7 +1427,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// the "add to dictionary" hint, we need to revert to suggestions - although it is unclear
// how we can come here if it's displayed.
if (suggestedWords.size() > 1 || typedWord.length() <= 1
- || null == mSuggestionStripView || isShowingAddToDictionaryHint()) {
+ || !hasSuggestionStripView() || isShowingAddToDictionaryHint()) {
return suggestedWords;
} else {
final SuggestedWords punctuationList =
@@ -1430,10 +1437,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final ArrayList<SuggestedWords.SuggestedWordInfo> typedWordAndPreviousSuggestions =
SuggestedWords.getTypedWordAndPreviousSuggestions(typedWord, oldSuggestedWords);
return new SuggestedWords(typedWordAndPreviousSuggestions, null /* rawSuggestions */,
- false /* typedWordValid */,
- false /* hasAutoCorrectionCandidate */,
- true /* isObsoleteSuggestions */,
- false /* isPrediction */);
+ false /* typedWordValid */, false /* hasAutoCorrectionCandidate */,
+ true /* isObsoleteSuggestions */, false /* isPrediction */);
}
}
@@ -1453,7 +1458,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
setNeutralSuggestionStrip();
} else {
mInputLogic.mWordComposer.setAutoCorrection(autoCorrection);
- setSuggestedWords(suggestedWords, isSuggestionsStripVisible());
+ setSuggestedWords(
+ suggestedWords, isSuggestionStripVisible(), true /* needsInputViewShown */);
}
// Cache the auto-correction in accessibility code so we can speak it if the user
// touches a key that will insert it.
@@ -1471,7 +1477,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override
public void showAddToDictionaryHint(final String word) {
- if (null == mSuggestionStripView) return;
+ if (!hasSuggestionStripView()) {
+ return;
+ }
mSuggestionStripView.showAddToDictionaryHint(word);
}
@@ -1480,13 +1488,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// punctuation suggestions (if it's disabled).
@Override
public void setNeutralSuggestionStrip() {
+ setNeutralSuggestionStripInternal(true /* needsInputViewShown */);
+ }
+
+ private void setNeutralSuggestionStripInternal(final boolean needsInputViewShown) {
final SettingsValues currentSettings = mSettings.getCurrent();
- if (currentSettings.mBigramPredictionEnabled) {
- setSuggestedWords(SuggestedWords.EMPTY, isSuggestionsStripVisible());
- } else {
- setSuggestedWords(currentSettings.mSpacingAndPunctuations.mSuggestPuncList,
- isSuggestionsStripVisible());
- }
+ final SuggestedWords neutralSuggestions = currentSettings.mBigramPredictionEnabled
+ ? SuggestedWords.EMPTY : currentSettings.mSpacingAndPunctuations.mSuggestPuncList;
+ setSuggestedWords(neutralSuggestions, isSuggestionStripVisible(), needsInputViewShown);
}
// TODO: Make this private
@@ -1612,13 +1621,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
launchSubActivity(SettingsActivity.class);
}
- public void launchKeyboardedDialogActivity(final Class<? extends Activity> activityClass) {
- // Put the text in the attached EditText into a safe, saved state before switching to a
- // new activity that will also use the soft keyboard.
- mInputLogic.commitTyped(mSettings.getCurrent(), LastComposedWord.NOT_A_SEPARATOR);
- launchSubActivity(activityClass);
- }
-
private void launchSubActivity(final Class<? extends Activity> activityClass) {
Intent intent = new Intent();
intent.setClass(LatinIME.this, activityClass);
@@ -1682,7 +1684,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// TODO: can this be removed somehow without breaking the tests?
@UsedForTesting
- /* package for test */ SuggestedWords getSuggestedWords() {
+ /* package for test */ SuggestedWords getSuggestedWordsForTest() {
// You may not use this method for anything else than debug
return DEBUG ? mInputLogic.mSuggestedWords : null;
}
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 0d0b7a160..ebad9bc0d 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -230,6 +230,9 @@ public final class RichInputConnection {
public void finishComposingText() {
if (DEBUG_BATCH_NESTING) checkBatchEdit();
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
+ // TODO: this is not correct! The cursor is not necessarily after the composing text.
+ // In the practice right now this is only called when input ends so it will be reset so
+ // it works, but it's wrong and should be fixed.
mCommittedTextBeforeComposingText.append(mComposingText);
mComposingText.setLength(0);
if (null != mIC) {
@@ -244,6 +247,9 @@ public final class RichInputConnection {
if (DEBUG_BATCH_NESTING) checkBatchEdit();
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
mCommittedTextBeforeComposingText.append(text);
+ // TODO: the following is exceedingly error-prone. Right now when the cursor is in the
+ // middle of the composing word mComposingText only holds the part of the composing text
+ // that is before the cursor, so this actually works, but it's terribly confusing. Fix this.
mExpectedSelStart += text.length() - mComposingText.length();
mExpectedSelEnd = mExpectedSelStart;
mComposingText.setLength(0);
@@ -347,6 +353,9 @@ public final class RichInputConnection {
public void deleteSurroundingText(final int beforeLength, final int afterLength) {
if (DEBUG_BATCH_NESTING) checkBatchEdit();
+ // TODO: the following is incorrect if the cursor is not immediately after the composition.
+ // Right now we never come here in this case because we reset the composing state before we
+ // come here in this case, but we need to fix this.
final int remainingChars = mComposingText.length() - beforeLength;
if (remainingChars >= 0) {
mComposingText.setLength(remainingChars);
@@ -447,8 +456,12 @@ public final class RichInputConnection {
getTextBeforeCursor(Constants.EDITOR_CONTENTS_CACHE_SIZE + (end - start), 0);
mCommittedTextBeforeComposingText.setLength(0);
if (!TextUtils.isEmpty(textBeforeCursor)) {
+ // The cursor is not necessarily at the end of the composing text, but we have its
+ // position in mExpectedSelStart and mExpectedSelEnd. In this case we want the start
+ // of the text, so we should use mExpectedSelStart. In other words, the composing
+ // text starts (mExpectedSelStart - start) characters before the end of textBeforeCursor
final int indexOfStartOfComposingText =
- Math.max(textBeforeCursor.length() - (end - start), 0);
+ Math.max(textBeforeCursor.length() - (mExpectedSelStart - start), 0);
mComposingText.append(textBeforeCursor.subSequence(indexOfStartOfComposingText,
textBeforeCursor.length()));
mCommittedTextBeforeComposingText.append(
@@ -544,6 +557,9 @@ public final class RichInputConnection {
final int checkLength = LOOKBACK_CHARACTER_NUM - 1;
final String reference = prev.length() <= checkLength ? prev.toString()
: prev.subSequence(prev.length() - checkLength, prev.length()).toString();
+ // TODO: right now the following works because mComposingText holds the part of the
+ // composing text that is before the cursor, but this is very confusing. We should
+ // fix it.
final StringBuilder internal = new StringBuilder()
.append(mCommittedTextBeforeComposingText).append(mComposingText);
if (internal.length() > checkLength) {
@@ -668,12 +684,16 @@ public final class RichInputConnection {
}
}
+ final boolean hasUrlSpans =
+ SpannableStringUtils.hasUrlSpans(before, startIndexInBefore, before.length())
+ || SpannableStringUtils.hasUrlSpans(after, 0, endIndexInAfter);
// We don't use TextUtils#concat because it copies all spans without respect to their
// nature. If the text includes a PARAGRAPH span and it has been split, then
// TextUtils#concat will crash when it tries to concat both sides of it.
return new TextRange(
SpannableStringUtils.concatWithNonParagraphSuggestionSpansOnly(before, after),
- startIndexInBefore, before.length() + endIndexInAfter, before.length());
+ startIndexInBefore, before.length() + endIndexInAfter, before.length(),
+ hasUrlSpans);
}
public boolean isCursorTouchingWord(final SpacingAndPunctuations spacingAndPunctuations) {
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 6f73859e8..3fc2cf8fd 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -198,7 +198,7 @@ public final class InputLogic {
final SuggestedWords suggestedWords = mSuggestedWords;
final String suggestion = suggestionInfo.mWord;
// If this is a punctuation picked from the suggestion strip, pass it to onCodeInput
- if (suggestion.length() == 1 && isShowingPunctuationList(settingsValues)) {
+ if (suggestion.length() == 1 && suggestedWords.isPunctuationSuggestions()) {
// Word separators are suggested before the user inputs something.
// So, LatinImeLogger logs "" as a user's input.
LatinImeLogger.logOnManualSuggestion("", suggestion, index, suggestedWords);
@@ -821,7 +821,7 @@ public final class InputLogic {
if (maybeDoubleSpacePeriod(settingsValues, handler)) {
keyboardSwitcher.updateShiftState();
mSpaceState = SpaceState.DOUBLE;
- } else if (!isShowingPunctuationList(settingsValues)) {
+ } else if (!mSuggestedWords.isPunctuationSuggestions()) {
mSpaceState = SpaceState.WEAK;
}
}
@@ -1270,6 +1270,10 @@ public final class InputLogic {
if (range.length() <= 0) return; // Race condition. No text to resume on, so bail out.
// If for some strange reason (editor bug or so) we measure the text before the cursor as
// longer than what the entire text is supposed to be, the safe thing to do is bail out.
+ if (range.mHasUrlSpans) return; // If there are links, we don't resume suggestions. Making
+ // edits to a linkified text through batch commands would ruin the URL spans, and unless
+ // we take very complicated steps to preserve the whole link, we can't do things right so
+ // we just do not resume because it's safer.
final int numberOfCharsInWordBeforeCursor = range.getNumberOfCharsInWordBeforeCursor();
if (numberOfCharsInWordBeforeCursor > expectedCursorPosition) return;
final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
@@ -1452,15 +1456,6 @@ public final class InputLogic {
}
/**
- * Find out if the punctuation list is shown in the suggestion strip.
- * @return whether the current suggestions are the punctuation list.
- */
- // TODO: make this private. It's used through LatinIME for tests.
- public boolean isShowingPunctuationList(final SettingsValues settingsValues) {
- return settingsValues.mSpacingAndPunctuations.mSuggestPuncList == mSuggestedWords;
- }
-
- /**
* Factor in auto-caps and manual caps and compute the current caps mode.
* @param settingsValues the current settings values.
* @param keyboardShiftMode the current shift mode of the keyboard. See
diff --git a/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java
index 1bc322724..bc856f113 100644
--- a/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java
@@ -17,14 +17,10 @@
package com.android.inputmethod.latin.makedict;
import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
-import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
-import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.TreeMap;
/**
@@ -35,34 +31,6 @@ public abstract class AbstractDictDecoder implements DictDecoder {
private static final int ERROR_CANNOT_READ = 1;
private static final int ERROR_WRONG_FORMAT = 2;
- protected DictionaryHeader readHeader(final DictBuffer headerBuffer)
- throws IOException, UnsupportedFormatException {
- if (headerBuffer == null) {
- openDictBuffer();
- }
-
- final int version = HeaderReader.readVersion(headerBuffer);
- if (version < FormatSpec.MINIMUM_SUPPORTED_VERSION
- || version > FormatSpec.MAXIMUM_SUPPORTED_VERSION) {
- throw new UnsupportedFormatException("Unsupported version : " + version);
- }
- // TODO: Remove this field.
- HeaderReader.readOptionFlags(headerBuffer);
- final int headerSize = HeaderReader.readHeaderSize(headerBuffer);
- if (headerSize < 0) {
- throw new UnsupportedFormatException("header size can't be negative.");
- }
-
- final HashMap<String, String> attributes = HeaderReader.readAttributes(headerBuffer,
- headerSize);
-
- final DictionaryHeader header = new DictionaryHeader(headerSize,
- new FusionDictionary.DictionaryOptions(attributes),
- new FormatOptions(version, DictionaryHeader.ATTRIBUTE_VALUE_TRUE.equals(
- attributes.get(DictionaryHeader.HAS_HISTORICAL_INFO_KEY))));
- return header;
- }
-
@Override @UsedForTesting
public int getTerminalPosition(final String word)
throws IOException, UnsupportedFormatException {
@@ -84,38 +52,6 @@ public abstract class AbstractDictDecoder implements DictDecoder {
}
/**
- * A utility class for reading a file header.
- */
- protected static class HeaderReader {
- protected static int readVersion(final DictBuffer dictBuffer)
- throws IOException, UnsupportedFormatException {
- return BinaryDictDecoderUtils.checkFormatVersion(dictBuffer);
- }
-
- protected static int readOptionFlags(final DictBuffer dictBuffer) {
- return dictBuffer.readUnsignedShort();
- }
-
- protected static int readHeaderSize(final DictBuffer dictBuffer) {
- return dictBuffer.readInt();
- }
-
- protected static HashMap<String, String> readAttributes(final DictBuffer dictBuffer,
- final int headerSize) {
- final HashMap<String, String> attributes = new HashMap<String, String>();
- while (dictBuffer.position() < headerSize) {
- // We can avoid an infinite loop here since dictBuffer.position() is always
- // increased by calling CharEncoding.readString.
- final String key = CharEncoding.readString(dictBuffer);
- final String value = CharEncoding.readString(dictBuffer);
- attributes.put(key, value);
- }
- dictBuffer.position(headerSize);
- return attributes;
- }
- }
-
- /**
* Check whether the header contains the expected information. This is a no-error method,
* that will return an error code and never throw a checked exception.
* @return an error code, either ERROR_* or SUCCESS.
@@ -148,7 +84,7 @@ public abstract class AbstractDictDecoder implements DictDecoder {
}
@Override
- public PtNodeInfo readPtNode(final int ptNodePos, final FormatOptions options) {
+ public PtNodeInfo readPtNode(final int ptNodePos) {
return null;
}
@@ -165,14 +101,4 @@ public abstract class AbstractDictDecoder implements DictDecoder {
public int readPtNodeCount() {
return 0;
}
-
- @Override
- public boolean readAndFollowForwardLink() {
- return false;
- }
-
- @Override
- public boolean hasNextPtNodeArray() {
- return false;
- }
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
index 845299c99..b534ebeff 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
@@ -17,18 +17,12 @@
package com.android.inputmethod.latin.makedict;
import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
-import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
-import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.TreeMap;
/**
* Decodes binary files for a FusionDictionary.
@@ -47,8 +41,6 @@ public final class BinaryDictDecoderUtils {
// This utility class is not publicly instantiable.
}
- private static final int MAX_JUMPS = 12;
-
@UsedForTesting
public interface DictBuffer {
public int readUnsignedByte();
@@ -296,60 +288,21 @@ public final class BinaryDictDecoderUtils {
* @param dictDecoder the dict decoder.
* @param headerSize the size of the header.
* @param pos the position to seek.
- * @param formatOptions file format options.
* @return the word with its frequency, as a weighted string.
*/
+ @UsedForTesting
/* package for tests */ static WeightedString getWordAtPosition(final DictDecoder dictDecoder,
- final int headerSize, final int pos, final FormatOptions formatOptions) {
+ final int headerSize, final int pos) {
final WeightedString result;
final int originalPos = dictDecoder.getPosition();
dictDecoder.setPosition(pos);
-
- if (BinaryDictIOUtils.supportsDynamicUpdate(formatOptions)) {
- result = getWordAtPositionWithParentAddress(dictDecoder, pos, formatOptions);
- } else {
- result = getWordAtPositionWithoutParentAddress(dictDecoder, headerSize, pos,
- formatOptions);
- }
-
+ result = getWordAtPositionWithoutParentAddress(dictDecoder, headerSize, pos);
dictDecoder.setPosition(originalPos);
return result;
}
- @SuppressWarnings("unused")
- private static WeightedString getWordAtPositionWithParentAddress(final DictDecoder dictDecoder,
- final int pos, final FormatOptions options) {
- int currentPos = pos;
- ProbabilityInfo probabilityInfo = null;
- final StringBuilder builder = new StringBuilder();
- // the length of the path from the root to the leaf is limited by MAX_WORD_LENGTH
- for (int count = 0; count < FormatSpec.MAX_WORD_LENGTH; ++count) {
- PtNodeInfo currentInfo;
- int loopCounter = 0;
- do {
- dictDecoder.setPosition(currentPos);
- currentInfo = dictDecoder.readPtNode(currentPos, options);
- if (BinaryDictIOUtils.isMovedPtNode(currentInfo.mFlags, options)) {
- currentPos = currentInfo.mParentAddress + currentInfo.mOriginalAddress;
- }
- if (DBG && loopCounter++ > MAX_JUMPS) {
- MakedictLog.d("Too many jumps - probably a bug");
- }
- } while (BinaryDictIOUtils.isMovedPtNode(currentInfo.mFlags, options));
- if (probabilityInfo == null) {
- probabilityInfo = currentInfo.mProbabilityInfo;
- }
- builder.insert(0,
- new String(currentInfo.mCharacters, 0, currentInfo.mCharacters.length));
- if (currentInfo.mParentAddress == FormatSpec.NO_PARENT_ADDRESS) break;
- currentPos = currentInfo.mParentAddress + currentInfo.mOriginalAddress;
- }
- return new WeightedString(builder.toString(), probabilityInfo);
- }
-
private static WeightedString getWordAtPositionWithoutParentAddress(
- final DictDecoder dictDecoder, final int headerSize, final int pos,
- final FormatOptions options) {
+ final DictDecoder dictDecoder, final int headerSize, final int pos) {
dictDecoder.setPosition(headerSize);
final int count = dictDecoder.readPtNodeCount();
int groupPos = dictDecoder.getPosition();
@@ -358,7 +311,7 @@ public final class BinaryDictDecoderUtils {
PtNodeInfo last = null;
for (int i = count - 1; i >= 0; --i) {
- PtNodeInfo info = dictDecoder.readPtNode(groupPos, options);
+ PtNodeInfo info = dictDecoder.readPtNode(groupPos);
groupPos = info.mEndAddress;
if (info.mOriginalAddress == pos) {
builder.append(new String(info.mCharacters, 0, info.mCharacters.length));
@@ -390,135 +343,6 @@ public final class BinaryDictDecoderUtils {
}
/**
- * Reads a single node array from a buffer.
- *
- * This methods reads the file at the current position. A node array is fully expected to start
- * at the current position.
- * This will recursively read other node arrays into the structure, populating the reverse
- * maps on the fly and using them to keep track of already read nodes.
- *
- * @param dictDecoder the dict decoder, correctly positioned at the start of a node array.
- * @param headerSize the size, in bytes, of the file header.
- * @param reverseNodeArrayMap a mapping from addresses to already read node arrays.
- * @param reversePtNodeMap a mapping from addresses to already read PtNodes.
- * @param options file format options.
- * @return the read node array with all his children already read.
- */
- private static PtNodeArray readNodeArray(final DictDecoder dictDecoder,
- final int headerSize, final Map<Integer, PtNodeArray> reverseNodeArrayMap,
- final Map<Integer, PtNode> reversePtNodeMap, final FormatOptions options)
- throws IOException {
- final ArrayList<PtNode> nodeArrayContents = new ArrayList<PtNode>();
- final int nodeArrayOriginPos = dictDecoder.getPosition();
-
- do { // Scan the linked-list node.
- final int count = dictDecoder.readPtNodeCount();
- int groupPos = dictDecoder.getPosition();
- for (int i = count; i > 0; --i) { // Scan the array of PtNode.
- PtNodeInfo info = dictDecoder.readPtNode(groupPos, options);
- if (BinaryDictIOUtils.isMovedPtNode(info.mFlags, options)) continue;
- ArrayList<WeightedString> shortcutTargets = info.mShortcutTargets;
- ArrayList<WeightedString> bigrams = null;
- if (null != info.mBigrams) {
- bigrams = new ArrayList<WeightedString>();
- for (PendingAttribute bigram : info.mBigrams) {
- final WeightedString word = getWordAtPosition(dictDecoder, headerSize,
- bigram.mAddress, options);
- final int reconstructedFrequency =
- BinaryDictIOUtils.reconstructBigramFrequency(word.getProbability(),
- bigram.mFrequency);
- bigrams.add(new WeightedString(word.mWord, reconstructedFrequency));
- }
- }
- if (BinaryDictIOUtils.hasChildrenAddress(info.mChildrenAddress)) {
- PtNodeArray children = reverseNodeArrayMap.get(info.mChildrenAddress);
- if (null == children) {
- final int currentPosition = dictDecoder.getPosition();
- dictDecoder.setPosition(info.mChildrenAddress);
- children = readNodeArray(dictDecoder, headerSize, reverseNodeArrayMap,
- reversePtNodeMap, options);
- dictDecoder.setPosition(currentPosition);
- }
- nodeArrayContents.add(
- new PtNode(info.mCharacters, shortcutTargets, bigrams,
- info.mProbabilityInfo,
- 0 != (info.mFlags & FormatSpec.FLAG_IS_NOT_A_WORD),
- 0 != (info.mFlags & FormatSpec.FLAG_IS_BLACKLISTED), children));
- } else {
- nodeArrayContents.add(
- new PtNode(info.mCharacters, shortcutTargets, bigrams,
- info.mProbabilityInfo,
- 0 != (info.mFlags & FormatSpec.FLAG_IS_NOT_A_WORD),
- 0 != (info.mFlags & FormatSpec.FLAG_IS_BLACKLISTED)));
- }
- groupPos = info.mEndAddress;
- }
-
- // reach the end of the array.
- if (options.supportsDynamicUpdate()) {
- final boolean hasValidForwardLink = dictDecoder.readAndFollowForwardLink();
- if (!hasValidForwardLink) break;
- }
- } while (options.supportsDynamicUpdate() && dictDecoder.hasNextPtNodeArray());
-
- final PtNodeArray nodeArray = new PtNodeArray(nodeArrayContents);
- nodeArray.mCachedAddressBeforeUpdate = nodeArrayOriginPos;
- nodeArray.mCachedAddressAfterUpdate = nodeArrayOriginPos;
- reverseNodeArrayMap.put(nodeArray.mCachedAddressAfterUpdate, nodeArray);
- return nodeArray;
- }
-
- /**
- * Helper function to get the binary format version from the header.
- * @throws IOException
- */
- private static int getFormatVersion(final DictBuffer dictBuffer)
- throws IOException {
- final int magic = dictBuffer.readInt();
- if (FormatSpec.MAGIC_NUMBER == magic) return dictBuffer.readUnsignedShort();
- return FormatSpec.NOT_A_VERSION_NUMBER;
- }
-
- /**
- * Helper function to get and validate the binary format version.
- * @throws UnsupportedFormatException
- * @throws IOException
- */
- static int checkFormatVersion(final DictBuffer dictBuffer)
- throws IOException, UnsupportedFormatException {
- final int version = getFormatVersion(dictBuffer);
- if (version < FormatSpec.MINIMUM_SUPPORTED_VERSION
- || version > FormatSpec.MAXIMUM_SUPPORTED_VERSION) {
- throw new UnsupportedFormatException("This file has version " + version
- + ", but this implementation does not support versions above "
- + FormatSpec.MAXIMUM_SUPPORTED_VERSION);
- }
- return version;
- }
-
- /**
- * Reads a buffer and returns the memory representation of the dictionary.
- *
- * This high-level method takes a buffer and reads its contents, populating a
- * FusionDictionary structure.
- *
- * @param dictDecoder the dict decoder.
- * @return the created dictionary.
- */
- @UsedForTesting
- /* package */ static FusionDictionary readDictionaryBinary(final DictDecoder dictDecoder)
- throws IOException, UnsupportedFormatException {
- // Read header
- final DictionaryHeader fileHeader = dictDecoder.readHeader();
-
- Map<Integer, PtNodeArray> reverseNodeArrayMapping = new TreeMap<Integer, PtNodeArray>();
- Map<Integer, PtNode> reversePtNodeMapping = new TreeMap<Integer, PtNode>();
- final PtNodeArray root = readNodeArray(dictDecoder, fileHeader.mBodyOffset,
- reverseNodeArrayMapping, reversePtNodeMapping, fileHeader.mFormatOptions);
- return new FusionDictionary(root, fileHeader.mDictionaryOptions);
- }
-
- /**
* Helper method to pass a file name instead of a File object to isBinaryDictionary.
*/
public static boolean isBinaryDictionary(final String filename) {
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
index 9c5d1b9d7..1593dea4f 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
@@ -122,18 +122,13 @@ public class BinaryDictEncoderUtils {
* Compute the maximum size of a PtNode, assuming 3-byte addresses for everything.
*
* @param ptNode the PtNode to compute the size of.
- * @param options file format options.
* @return the maximum size of the PtNode.
*/
- private static int getPtNodeMaximumSize(final PtNode ptNode, final FormatOptions options) {
- int size = getNodeHeaderSize(ptNode, options);
+ private static int getPtNodeMaximumSize(final PtNode ptNode) {
+ int size = getNodeHeaderSize(ptNode);
if (ptNode.isTerminal()) {
- // If terminal, one byte for the frequency or four bytes for the terminal id.
- if (options.mHasTerminalId) {
- size += FormatSpec.PTNODE_TERMINAL_ID_SIZE;
- } else {
- size += FormatSpec.PTNODE_FREQUENCY_SIZE;
- }
+ // If terminal, one byte for the frequency.
+ size += FormatSpec.PTNODE_FREQUENCY_SIZE;
}
size += FormatSpec.PTNODE_MAX_ADDRESS_SIZE; // For children address
size += getShortcutListSize(ptNode.mShortcutTargets);
@@ -151,19 +146,14 @@ public class BinaryDictEncoderUtils {
* the containing node array, and cache it it its 'mCachedSize' member.
*
* @param ptNodeArray the node array to compute the maximum size of.
- * @param options file format options.
*/
- private static void calculatePtNodeArrayMaximumSize(final PtNodeArray ptNodeArray,
- final FormatOptions options) {
+ private static void calculatePtNodeArrayMaximumSize(final PtNodeArray ptNodeArray) {
int size = getPtNodeCountSize(ptNodeArray);
for (PtNode node : ptNodeArray.mData) {
- final int nodeSize = getPtNodeMaximumSize(node, options);
+ final int nodeSize = getPtNodeMaximumSize(node);
node.mCachedSize = nodeSize;
size += nodeSize;
}
- if (options.supportsDynamicUpdate()) {
- size += FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
- }
ptNodeArray.mCachedSize = size;
}
@@ -171,15 +161,9 @@ public class BinaryDictEncoderUtils {
* Compute the size of the header (flag + [parent address] + characters size) of a PtNode.
*
* @param ptNode the PtNode of which to compute the size of the header
- * @param options file format options.
*/
- private static int getNodeHeaderSize(final PtNode ptNode, final FormatOptions options) {
- if (BinaryDictIOUtils.supportsDynamicUpdate(options)) {
- return FormatSpec.PTNODE_FLAGS_SIZE + FormatSpec.PARENT_ADDRESS_SIZE
- + getPtNodeCharactersSize(ptNode);
- } else {
- return FormatSpec.PTNODE_FLAGS_SIZE + getPtNodeCharactersSize(ptNode);
- }
+ private static int getNodeHeaderSize(final PtNode ptNode) {
+ return FormatSpec.PTNODE_FLAGS_SIZE + getPtNodeCharactersSize(ptNode);
}
/**
@@ -379,11 +363,10 @@ public class BinaryDictEncoderUtils {
*
* @param ptNodeArray the node array to compute the size of.
* @param dict the dictionary in which the word/attributes are to be found.
- * @param formatOptions file format options.
* @return false if none of the cached addresses inside the node array changed, true otherwise.
*/
private static boolean computeActualPtNodeArraySize(final PtNodeArray ptNodeArray,
- final FusionDictionary dict, final FormatOptions formatOptions) {
+ final FusionDictionary dict) {
boolean changed = false;
int size = getPtNodeCountSize(ptNodeArray);
for (PtNode ptNode : ptNodeArray.mData) {
@@ -391,37 +374,26 @@ public class BinaryDictEncoderUtils {
if (ptNode.mCachedAddressAfterUpdate != ptNode.mCachedAddressBeforeUpdate) {
changed = true;
}
- int nodeSize = getNodeHeaderSize(ptNode, formatOptions);
+ int nodeSize = getNodeHeaderSize(ptNode);
if (ptNode.isTerminal()) {
- if (formatOptions.mHasTerminalId) {
- nodeSize += FormatSpec.PTNODE_TERMINAL_ID_SIZE;
- } else {
- nodeSize += FormatSpec.PTNODE_FREQUENCY_SIZE;
- }
+ nodeSize += FormatSpec.PTNODE_FREQUENCY_SIZE;
}
- if (formatOptions.supportsDynamicUpdate()) {
- nodeSize += FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
- } else if (null != ptNode.mChildren) {
+ if (null != ptNode.mChildren) {
nodeSize += getByteSize(getOffsetToTargetNodeArrayDuringUpdate(ptNodeArray,
nodeSize + size, ptNode.mChildren));
}
- if (formatOptions.mVersion < FormatSpec.FIRST_VERSION_WITH_TERMINAL_ID) {
- nodeSize += getShortcutListSize(ptNode.mShortcutTargets);
- if (null != ptNode.mBigrams) {
- for (WeightedString bigram : ptNode.mBigrams) {
- final int offset = getOffsetToTargetPtNodeDuringUpdate(ptNodeArray,
- nodeSize + size + FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE,
- FusionDictionary.findWordInTree(dict.mRootNodeArray, bigram.mWord));
- nodeSize += getByteSize(offset) + FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE;
- }
+ nodeSize += getShortcutListSize(ptNode.mShortcutTargets);
+ if (null != ptNode.mBigrams) {
+ for (WeightedString bigram : ptNode.mBigrams) {
+ final int offset = getOffsetToTargetPtNodeDuringUpdate(ptNodeArray,
+ nodeSize + size + FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE,
+ FusionDictionary.findWordInTree(dict.mRootNodeArray, bigram.mWord));
+ nodeSize += getByteSize(offset) + FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE;
}
}
ptNode.mCachedSize = nodeSize;
size += nodeSize;
}
- if (formatOptions.supportsDynamicUpdate()) {
- size += FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
- }
if (ptNodeArray.mCachedSize != size) {
ptNodeArray.mCachedSize = size;
changed = true;
@@ -433,11 +405,10 @@ public class BinaryDictEncoderUtils {
* Initializes the cached addresses of node arrays and their containing nodes from their size.
*
* @param flatNodes the list of node arrays.
- * @param formatOptions file format options.
* @return the byte size of the entire stack.
*/
- private static int initializePtNodeArraysCachedAddresses(final ArrayList<PtNodeArray> flatNodes,
- final FormatOptions formatOptions) {
+ private static int initializePtNodeArraysCachedAddresses(
+ final ArrayList<PtNodeArray> flatNodes) {
int nodeArrayOffset = 0;
for (final PtNodeArray nodeArray : flatNodes) {
nodeArray.mCachedAddressBeforeUpdate = nodeArrayOffset;
@@ -468,28 +439,6 @@ public class BinaryDictEncoderUtils {
}
/**
- * Compute the cached parent addresses after all has been updated.
- *
- * The parent addresses are used by some binary formats at write-to-disk time. Not all formats
- * need them. In particular, version 2 does not need them, and version 3 does.
- *
- * @param flatNodes the flat array of node arrays to fill in
- */
- private static void computeParentAddresses(final ArrayList<PtNodeArray> flatNodes) {
- for (final PtNodeArray nodeArray : flatNodes) {
- for (final PtNode ptNode : nodeArray.mData) {
- if (null != ptNode.mChildren) {
- // Assign my address to children's parent address
- // Here BeforeUpdate and AfterUpdate addresses have the same value, so it
- // does not matter which we use.
- ptNode.mChildren.mCachedParentAddress = ptNode.mCachedAddressAfterUpdate
- - ptNode.mChildren.mCachedAddressAfterUpdate;
- }
- }
- }
- }
-
- /**
* Compute the addresses and sizes of an ordered list of PtNode arrays.
*
* This method takes a list of PtNode arrays and will update their cached address and size
@@ -501,14 +450,15 @@ public class BinaryDictEncoderUtils {
*
* @param dict the dictionary
* @param flatNodes the ordered list of PtNode arrays
- * @param formatOptions file format options.
* @return the same array it was passed. The nodes have been updated for address and size.
*/
/* package */ static ArrayList<PtNodeArray> computeAddresses(final FusionDictionary dict,
- final ArrayList<PtNodeArray> flatNodes, final FormatOptions formatOptions) {
+ final ArrayList<PtNodeArray> flatNodes) {
// First get the worst possible sizes and offsets
- for (final PtNodeArray n : flatNodes) calculatePtNodeArrayMaximumSize(n, formatOptions);
- final int offset = initializePtNodeArraysCachedAddresses(flatNodes, formatOptions);
+ for (final PtNodeArray n : flatNodes) {
+ calculatePtNodeArrayMaximumSize(n);
+ }
+ final int offset = initializePtNodeArraysCachedAddresses(flatNodes);
MakedictLog.i("Compressing the array addresses. Original size : " + offset);
MakedictLog.i("(Recursively seen size : " + offset + ")");
@@ -521,8 +471,7 @@ public class BinaryDictEncoderUtils {
for (final PtNodeArray ptNodeArray : flatNodes) {
ptNodeArray.mCachedAddressAfterUpdate = ptNodeArrayStartOffset;
final int oldNodeArraySize = ptNodeArray.mCachedSize;
- final boolean changed =
- computeActualPtNodeArraySize(ptNodeArray, dict, formatOptions);
+ final boolean changed = computeActualPtNodeArraySize(ptNodeArray, dict);
final int newNodeArraySize = ptNodeArray.mCachedSize;
if (oldNodeArraySize < newNodeArraySize) {
throw new RuntimeException("Increased size ?!");
@@ -535,9 +484,6 @@ public class BinaryDictEncoderUtils {
if (passes > MAX_PASSES) throw new RuntimeException("Too many passes - probably a bug");
} while (changesDone);
- if (formatOptions.supportsDynamicUpdate()) {
- computeParentAddresses(flatNodes);
- }
final PtNodeArray lastPtNodeArray = flatNodes.get(flatNodes.size() - 1);
MakedictLog.i("Compression complete in " + passes + " passes.");
MakedictLog.i("After address compression : "
@@ -634,35 +580,29 @@ public class BinaryDictEncoderUtils {
* @param hasBigrams whether the PtNode has bigrams.
* @param isNotAWord whether the PtNode is not a word.
* @param isBlackListEntry whether the PtNode is a blacklist entry.
- * @param formatOptions file format options.
* @return the flags
*/
static int makePtNodeFlags(final boolean hasMultipleChars, final boolean isTerminal,
final int childrenAddressSize, final boolean hasShortcuts, final boolean hasBigrams,
- final boolean isNotAWord, final boolean isBlackListEntry,
- final FormatOptions formatOptions) {
+ final boolean isNotAWord, final boolean isBlackListEntry) {
byte flags = 0;
if (hasMultipleChars) flags |= FormatSpec.FLAG_HAS_MULTIPLE_CHARS;
if (isTerminal) flags |= FormatSpec.FLAG_IS_TERMINAL;
- if (formatOptions.supportsDynamicUpdate()) {
- flags |= FormatSpec.FLAG_IS_NOT_MOVED;
- } else if (true) {
- switch (childrenAddressSize) {
- case 1:
- flags |= FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE;
- break;
- case 2:
- flags |= FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES;
- break;
- case 3:
- flags |= FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES;
- break;
- case 0:
- flags |= FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS;
- break;
- default:
- throw new RuntimeException("Node with a strange address");
- }
+ switch (childrenAddressSize) {
+ case 1:
+ flags |= FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE;
+ break;
+ case 2:
+ flags |= FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES;
+ break;
+ case 3:
+ flags |= FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES;
+ break;
+ case 0:
+ flags |= FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS;
+ break;
+ default:
+ throw new RuntimeException("Node with a strange address");
}
if (hasShortcuts) flags |= FormatSpec.FLAG_HAS_SHORTCUT_TARGETS;
if (hasBigrams) flags |= FormatSpec.FLAG_HAS_BIGRAMS;
@@ -671,12 +611,12 @@ public class BinaryDictEncoderUtils {
return flags;
}
- /* package */ static byte makePtNodeFlags(final PtNode node, final int childrenOffset,
- final FormatOptions formatOptions) {
+ /* package */ static byte makePtNodeFlags(final PtNode node, final int childrenOffset) {
return (byte) makePtNodeFlags(node.mChars.length > 1, node.isTerminal(),
getByteSize(childrenOffset),
node.mShortcutTargets != null && !node.mShortcutTargets.isEmpty(),
- node.mBigrams != null, node.mIsNotAWord, node.mIsBlacklistEntry, formatOptions);
+ node.mBigrams != null && !node.mBigrams.isEmpty(),
+ node.mIsNotAWord, node.mIsBlacklistEntry);
}
/**
@@ -767,38 +707,14 @@ public class BinaryDictEncoderUtils {
+ (frequency & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY);
}
- /* package */ static final int writeParentAddress(final byte[] buffer, final int index,
- final int address, final FormatOptions formatOptions) {
- if (BinaryDictIOUtils.supportsDynamicUpdate(formatOptions)) {
- if (address == FormatSpec.NO_PARENT_ADDRESS) {
- buffer[index] = buffer[index + 1] = buffer[index + 2] = 0;
- } else {
- final int absAddress = Math.abs(address);
- assert(absAddress <= FormatSpec.SINT24_MAX);
- buffer[index] = (byte)((address < 0 ? FormatSpec.MSB8 : 0)
- | ((absAddress >> 16) & 0xFF));
- buffer[index + 1] = (byte)((absAddress >> 8) & 0xFF);
- buffer[index + 2] = (byte)(absAddress & 0xFF);
- }
- return index + 3;
- } else {
- return index;
- }
- }
-
- /* package */ static final int getChildrenPosition(final PtNode ptNode,
- final FormatOptions formatOptions) {
+ /* package */ static final int getChildrenPosition(final PtNode ptNode) {
int positionOfChildrenPosField = ptNode.mCachedAddressAfterUpdate
- + getNodeHeaderSize(ptNode, formatOptions);
+ + getNodeHeaderSize(ptNode);
if (ptNode.isTerminal()) {
- // A terminal node has either the terminal id or the frequency.
+ // A terminal node has the frequency.
// If positionOfChildrenPosField is incorrect, we may crash when jumping to the children
// position.
- if (formatOptions.mHasTerminalId) {
- positionOfChildrenPosField += FormatSpec.PTNODE_TERMINAL_ID_SIZE;
- } else {
- positionOfChildrenPosField += FormatSpec.PTNODE_FREQUENCY_SIZE;
- }
+ positionOfChildrenPosField += FormatSpec.PTNODE_FREQUENCY_SIZE;
}
return null == ptNode.mChildren ? FormatSpec.NO_CHILDREN_ADDRESS
: ptNode.mChildren.mCachedAddressAfterUpdate - positionOfChildrenPosField;
@@ -810,12 +726,10 @@ public class BinaryDictEncoderUtils {
* @param dict the dictionary the node array is a part of (for relative offsets).
* @param dictEncoder the dictionary encoder.
* @param ptNodeArray the node array to write.
- * @param formatOptions file format options.
*/
@SuppressWarnings("unused")
/* package */ static void writePlacedPtNodeArray(final FusionDictionary dict,
- final DictEncoder dictEncoder, final PtNodeArray ptNodeArray,
- final FormatOptions formatOptions) {
+ final DictEncoder dictEncoder, final PtNodeArray ptNodeArray) {
// TODO: Make the code in common with BinaryDictIOUtils#writePtNode
dictEncoder.setPosition(ptNodeArray.mCachedAddressAfterUpdate);
@@ -838,10 +752,7 @@ public class BinaryDictEncoderUtils {
+ FormatSpec.MAX_TERMINAL_FREQUENCY
+ " : " + ptNode.mProbabilityInfo.toString());
}
- dictEncoder.writePtNode(ptNode, parentPosition, formatOptions, dict);
- }
- if (formatOptions.supportsDynamicUpdate()) {
- dictEncoder.writeForwardLinkAddress(FormatSpec.NO_FORWARD_LINK_ADDRESS);
+ dictEncoder.writePtNode(ptNode, dict);
}
if (dictEncoder.getPosition() != ptNodeArray.mCachedAddressAfterUpdate
+ ptNodeArray.mCachedSize) {
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
index 54446df49..989ca4b2f 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
@@ -18,7 +18,6 @@ package com.android.inputmethod.latin.makedict;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.Constants;
-import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
import com.android.inputmethod.latin.utils.ByteArrayDictBuffer;
@@ -60,8 +59,7 @@ public final class BinaryDictIOUtils {
private static void readUnigramsAndBigramsBinaryInner(final DictDecoder dictDecoder,
final int bodyOffset, final Map<Integer, String> words,
final Map<Integer, Integer> frequencies,
- final Map<Integer, ArrayList<PendingAttribute>> bigrams,
- final FormatOptions formatOptions) {
+ final Map<Integer, ArrayList<PendingAttribute>> bigrams) {
int[] pushedChars = new int[FormatSpec.MAX_WORD_LENGTH + 1];
Stack<Position> stack = new Stack<Position>();
@@ -90,17 +88,12 @@ public final class BinaryDictIOUtils {
stack.pop();
continue;
}
- final PtNodeInfo ptNodeInfo = dictDecoder.readPtNode(p.mAddress, formatOptions);
+ final PtNodeInfo ptNodeInfo = dictDecoder.readPtNode(p.mAddress);
for (int i = 0; i < ptNodeInfo.mCharacters.length; ++i) {
pushedChars[index++] = ptNodeInfo.mCharacters[i];
}
p.mPosition++;
-
- final boolean isMovedPtNode = isMovedPtNode(ptNodeInfo.mFlags,
- formatOptions);
- final boolean isDeletedPtNode = isDeletedPtNode(ptNodeInfo.mFlags,
- formatOptions);
- if (!isMovedPtNode && !isDeletedPtNode && ptNodeInfo.isTerminal()) {// found word
+ if (ptNodeInfo.isTerminal()) {// found word
words.put(ptNodeInfo.mOriginalAddress, new String(pushedChars, 0, index));
frequencies.put(
ptNodeInfo.mOriginalAddress, ptNodeInfo.mProbabilityInfo.mProbability);
@@ -110,25 +103,13 @@ public final class BinaryDictIOUtils {
}
if (p.mPosition == p.mNumOfPtNode) {
- if (formatOptions.supportsDynamicUpdate()) {
- final boolean hasValidForwardLinkAddress =
- dictDecoder.readAndFollowForwardLink();
- if (hasValidForwardLinkAddress && dictDecoder.hasNextPtNodeArray()) {
- // The node array has a forward link.
- p.mNumOfPtNode = Position.NOT_READ_PTNODE_COUNT;
- p.mAddress = dictDecoder.getPosition();
- } else {
- stack.pop();
- }
- } else {
- stack.pop();
- }
+ stack.pop();
} else {
- // The Ptnode array has more PtNodes.
+ // The PtNode array has more PtNodes.
p.mAddress = dictDecoder.getPosition();
}
- if (!isMovedPtNode && hasChildrenAddress(ptNodeInfo.mChildrenAddress)) {
+ if (hasChildrenAddress(ptNodeInfo.mChildrenAddress)) {
final Position childrenPos = new Position(ptNodeInfo.mChildrenAddress, index);
stack.push(childrenPos);
}
@@ -153,7 +134,7 @@ public final class BinaryDictIOUtils {
// Read header
final DictionaryHeader header = dictDecoder.readHeader();
readUnigramsAndBigramsBinaryInner(dictDecoder, header.mBodyOffset, words,
- frequencies, bigrams, header.mFormatOptions);
+ frequencies, bigrams);
}
/**
@@ -171,8 +152,7 @@ public final class BinaryDictIOUtils {
final String word) throws IOException, UnsupportedFormatException {
if (word == null) return FormatSpec.NOT_VALID_WORD;
dictDecoder.setPosition(0);
-
- final DictionaryHeader header = dictDecoder.readHeader();
+ dictDecoder.readHeader();
int wordPos = 0;
final int wordLen = word.codePointCount(0, word.length());
for (int depth = 0; depth < Constants.DICTIONARY_MAX_WORD_LENGTH; ++depth) {
@@ -183,13 +163,7 @@ public final class BinaryDictIOUtils {
boolean foundNextPtNode = false;
for (int i = 0; i < ptNodeCount; ++i) {
final int ptNodePos = dictDecoder.getPosition();
- final PtNodeInfo currentInfo = dictDecoder.readPtNode(ptNodePos,
- header.mFormatOptions);
- final boolean isMovedNode = isMovedPtNode(currentInfo.mFlags,
- header.mFormatOptions);
- final boolean isDeletedNode = isDeletedPtNode(currentInfo.mFlags,
- header.mFormatOptions);
- if (isMovedNode) continue;
+ final PtNodeInfo currentInfo = dictDecoder.readPtNode(ptNodePos);
boolean same = true;
for (int p = 0, j = word.offsetByCodePoints(0, wordPos);
p < currentInfo.mCharacters.length;
@@ -204,7 +178,7 @@ public final class BinaryDictIOUtils {
if (same) {
// found the PtNode matches the word.
if (wordPos + currentInfo.mCharacters.length == wordLen) {
- if (!currentInfo.isTerminal() || isDeletedNode) {
+ if (!currentInfo.isTerminal()) {
return FormatSpec.NOT_VALID_WORD;
} else {
return ptNodePos;
@@ -219,65 +193,14 @@ public final class BinaryDictIOUtils {
break;
}
}
-
- // If we found the next PtNode, it is under the file pointer.
- // But if not, we are at the end of this node array so we expect to have
- // a forward link address that we need to consult and possibly resume
- // search on the next node array in the linked list.
if (foundNextPtNode) break;
- if (!header.mFormatOptions.supportsDynamicUpdate()) {
- return FormatSpec.NOT_VALID_WORD;
- }
-
- final boolean hasValidForwardLinkAddress =
- dictDecoder.readAndFollowForwardLink();
- if (!hasValidForwardLinkAddress || !dictDecoder.hasNextPtNodeArray()) {
- return FormatSpec.NOT_VALID_WORD;
- }
+ return FormatSpec.NOT_VALID_WORD;
} while(true);
}
return FormatSpec.NOT_VALID_WORD;
}
/**
- * @return the size written, in bytes. Always 3 bytes.
- */
- @UsedForTesting
- static int writeSInt24ToBuffer(final DictBuffer dictBuffer, final int value) {
- final int absValue = Math.abs(value);
- dictBuffer.put((byte)(((value < 0 ? 0x80 : 0) | (absValue >> 16)) & 0xFF));
- dictBuffer.put((byte)((absValue >> 8) & 0xFF));
- dictBuffer.put((byte)(absValue & 0xFF));
- return 3;
- }
-
- /**
- * @return the size written, in bytes. Always 3 bytes.
- */
- @UsedForTesting
- static int writeSInt24ToStream(final OutputStream destination, final int value)
- throws IOException {
- final int absValue = Math.abs(value);
- destination.write((byte)(((value < 0 ? 0x80 : 0) | (absValue >> 16)) & 0xFF));
- destination.write((byte)((absValue >> 8) & 0xFF));
- destination.write((byte)(absValue & 0xFF));
- return 3;
- }
-
- @UsedForTesting
- static void skipString(final DictBuffer dictBuffer,
- final boolean hasMultipleChars) {
- if (hasMultipleChars) {
- int character = CharEncoding.readChar(dictBuffer);
- while (character != FormatSpec.INVALID_CHARACTER) {
- character = CharEncoding.readChar(dictBuffer);
- }
- } else {
- CharEncoding.readChar(dictBuffer);
- }
- }
-
- /**
* Writes a PtNodeCount to the stream.
*
* @param destination the stream to write.
@@ -356,30 +279,6 @@ public final class BinaryDictIOUtils {
}
/**
- * Helper method to check whether the node is moved.
- */
- public static boolean isMovedPtNode(final int flags, final FormatOptions options) {
- return options.supportsDynamicUpdate()
- && ((flags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) == FormatSpec.FLAG_IS_MOVED);
- }
-
- /**
- * Helper method to check whether the dictionary can be updated dynamically.
- */
- public static boolean supportsDynamicUpdate(final FormatOptions options) {
- return options.mVersion >= FormatSpec.FIRST_VERSION_WITH_DYNAMIC_UPDATE
- && options.supportsDynamicUpdate();
- }
-
- /**
- * Helper method to check whether the node is deleted.
- */
- public static boolean isDeletedPtNode(final int flags, final FormatOptions formatOptions) {
- return formatOptions.supportsDynamicUpdate()
- && ((flags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) == FormatSpec.FLAG_IS_DELETED);
- }
-
- /**
* Compute the binary size of the node count
* @param count the node count
* @return the size of the node count, either 1 or 2 bytes.
@@ -396,9 +295,7 @@ public final class BinaryDictIOUtils {
}
}
- static int getChildrenAddressSize(final int optionFlags,
- final FormatOptions formatOptions) {
- if (formatOptions.supportsDynamicUpdate()) return FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
+ static int getChildrenAddressSize(final int optionFlags) {
switch (optionFlags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) {
case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE:
return 1;
@@ -419,6 +316,7 @@ public final class BinaryDictIOUtils {
* @param bigramFrequency compressed frequency
* @return approximate bigram frequency
*/
+ @UsedForTesting
public static int reconstructBigramFrequency(final int unigramFrequency,
final int bigramFrequency) {
final float stepSize = (FormatSpec.MAX_TERMINAL_FREQUENCY - unigramFrequency)
diff --git a/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java
index 0fb98aba9..a3b28a702 100644
--- a/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java
@@ -18,7 +18,6 @@ package com.android.inputmethod.latin.makedict;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
-import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
import com.android.inputmethod.latin.utils.ByteArrayDictBuffer;
import java.io.File;
@@ -45,10 +44,9 @@ public interface DictDecoder {
/**
* Reads PtNode from ptNodePos.
* @param ptNodePos the position of PtNode.
- * @param formatOptions the format options.
* @return PtNodeInfo.
*/
- public PtNodeInfo readPtNode(final int ptNodePos, final FormatOptions formatOptions);
+ public PtNodeInfo readPtNode(final int ptNodePos);
/**
* Reads a buffer and returns the memory representation of the dictionary.
@@ -113,14 +111,6 @@ public interface DictDecoder {
public int readPtNodeCount();
/**
- * Reads the forward link and advances the position.
- *
- * @return true if this method moves the file pointer, false otherwise.
- */
- public boolean readAndFollowForwardLink();
- public boolean hasNextPtNodeArray();
-
- /**
* Opens the dictionary file and makes DictBuffer.
*/
@UsedForTesting
diff --git a/java/src/com/android/inputmethod/latin/makedict/DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/DictEncoder.java
index ea5d492d8..a5dc45691 100644
--- a/java/src/com/android/inputmethod/latin/makedict/DictEncoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/DictEncoder.java
@@ -32,7 +32,5 @@ public interface DictEncoder {
public int getPosition();
public void writePtNodeCount(final int ptNodeCount);
public void writeForwardLinkAddress(final int forwardLinkAddress);
-
- public void writePtNode(final PtNode ptNode, final int parentPosition,
- final FormatOptions formatOptions, final FusionDictionary dict);
+ public void writePtNode(final PtNode ptNode, final FusionDictionary dict);
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index 5a3807389..c7635eff9 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -309,7 +309,6 @@ public final class FormatSpec {
*/
public static final class FormatOptions {
public final int mVersion;
- public final boolean mHasTerminalId;
public final boolean mHasTimestamp;
@UsedForTesting
@@ -319,13 +318,8 @@ public final class FormatSpec {
public FormatOptions(final int version, final boolean hasTimestamp) {
mVersion = version;
- mHasTerminalId = (version >= FIRST_VERSION_WITH_TERMINAL_ID);
mHasTimestamp = hasTimestamp;
}
-
- public boolean supportsDynamicUpdate() {
- return mVersion >= FIRST_VERSION_WITH_DYNAMIC_UPDATE;
- }
}
/**
diff --git a/java/src/com/android/inputmethod/latin/makedict/PtNodeInfo.java b/java/src/com/android/inputmethod/latin/makedict/PtNodeInfo.java
index 4760aa8d5..f52117c6c 100644
--- a/java/src/com/android/inputmethod/latin/makedict/PtNodeInfo.java
+++ b/java/src/com/android/inputmethod/latin/makedict/PtNodeInfo.java
@@ -31,12 +31,11 @@ public final class PtNodeInfo {
public final int[] mCharacters;
public final ProbabilityInfo mProbabilityInfo;
public final int mChildrenAddress;
- public final int mParentAddress;
public final ArrayList<WeightedString> mShortcutTargets;
public final ArrayList<PendingAttribute> mBigrams;
public PtNodeInfo(final int originalAddress, final int endAddress, final int flags,
- final int[] characters, final ProbabilityInfo probabilityInfo, final int parentAddress,
+ final int[] characters, final ProbabilityInfo probabilityInfo,
final int childrenAddress, final ArrayList<WeightedString> shortcutTargets,
final ArrayList<PendingAttribute> bigrams) {
mOriginalAddress = originalAddress;
@@ -44,7 +43,6 @@ public final class PtNodeInfo {
mFlags = flags;
mCharacters = characters;
mProbabilityInfo = probabilityInfo;
- mParentAddress = parentAddress;
mChildrenAddress = childrenAddress;
mShortcutTargets = shortcutTargets;
mBigrams = bigrams;
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
index 5dda715a6..315bd8e58 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
@@ -17,12 +17,11 @@
package com.android.inputmethod.latin.makedict;
import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.BinaryDictionary;
import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
-import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
-
-import android.util.Log;
+import com.android.inputmethod.latin.utils.CollectionUtils;
import java.io.File;
import java.io.FileNotFoundException;
@@ -33,6 +32,7 @@ import java.util.Arrays;
/**
* An implementation of DictDecoder for version 2 binary dictionary.
*/
+// TODO: Separate logics that are used only for testing.
@UsedForTesting
public class Ver2DictDecoder extends AbstractDictDecoder {
private static final String TAG = Ver2DictDecoder.class.getSimpleName();
@@ -116,12 +116,19 @@ public class Ver2DictDecoder extends AbstractDictDecoder {
}
protected final File mDictionaryBinaryFile;
+ // TODO: Remove mBufferFactory and mDictBuffer from this class members because they are now
+ // used only for testing.
private final DictionaryBufferFactory mBufferFactory;
protected DictBuffer mDictBuffer;
+ private final BinaryDictionary mBinaryDictionary;
/* package */ Ver2DictDecoder(final File file, final int factoryFlag) {
mDictionaryBinaryFile = file;
mDictBuffer = null;
+ // dictType is not being used in dicttool. Passing an empty string.
+ mBinaryDictionary = new BinaryDictionary(file.getAbsolutePath(),
+ 0 /* offset */, file.length() /* length */, true /* useFullEditDistance */,
+ null /* locale */, "" /* dictType */, false /* isUpdatable */);
if ((factoryFlag & MASK_DICTBUFFER) == USE_READONLY_BYTEBUFFER) {
mBufferFactory = new DictionaryBufferFromReadOnlyByteBufferFactory();
@@ -137,6 +144,10 @@ public class Ver2DictDecoder extends AbstractDictDecoder {
/* package */ Ver2DictDecoder(final File file, final DictionaryBufferFactory factory) {
mDictionaryBinaryFile = file;
mBufferFactory = factory;
+ // dictType is not being used in dicttool. Passing an empty string.
+ mBinaryDictionary = new BinaryDictionary(file.getAbsolutePath(),
+ 0 /* offset */, file.length() /* length */, true /* useFullEditDistance */,
+ null /* locale */, "" /* dictType */, false /* isUpdatable */);
}
@Override
@@ -161,21 +172,23 @@ public class Ver2DictDecoder extends AbstractDictDecoder {
@Override
public DictionaryHeader readHeader() throws IOException, UnsupportedFormatException {
- if (mDictBuffer == null) {
- openDictBuffer();
+ final DictionaryHeader header = mBinaryDictionary.getHeader();
+ if (header.mFormatOptions.mVersion != FormatSpec.VERSION2) {
+ throw new UnsupportedFormatException("File header has a wrong version : "
+ + header.mFormatOptions.mVersion);
}
- final DictionaryHeader header = super.readHeader(mDictBuffer);
- final int version = header.mFormatOptions.mVersion;
- if (version != FormatSpec.VERSION2) {
- throw new UnsupportedFormatException("File header has a wrong version : " + version);
+ if (!isDictBufferOpen()) {
+ openDictBuffer();
}
+ // Advance buffer reading position to the head of dictionary body.
+ setPosition(header.mBodyOffset);
return header;
}
// TODO: Make this buffer multi thread safe.
private final int[] mCharacterBuffer = new int[FormatSpec.MAX_WORD_LENGTH];
@Override
- public PtNodeInfo readPtNode(final int ptNodePos, final FormatOptions options) {
+ public PtNodeInfo readPtNode(final int ptNodePos) {
int addressPointer = ptNodePos;
final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer);
addressPointer += FormatSpec.PTNODE_FLAGS_SIZE;
@@ -209,7 +222,7 @@ public class Ver2DictDecoder extends AbstractDictDecoder {
if (childrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) {
childrenAddress += addressPointer;
}
- addressPointer += BinaryDictIOUtils.getChildrenAddressSize(flags, options);
+ addressPointer += BinaryDictIOUtils.getChildrenAddressSize(flags);
final ArrayList<WeightedString> shortcutTargets;
if (0 != (flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS)) {
// readShortcut will add shortcuts to shortcutTargets.
@@ -232,30 +245,53 @@ public class Ver2DictDecoder extends AbstractDictDecoder {
bigrams = null;
}
return new PtNodeInfo(ptNodePos, addressPointer, flags, characters, probabilityInfo,
- FormatSpec.NO_PARENT_ADDRESS, childrenAddress, shortcutTargets, bigrams);
+ childrenAddress, shortcutTargets, bigrams);
}
@Override
public FusionDictionary readDictionaryBinary(final boolean deleteDictIfBroken)
throws FileNotFoundException, IOException, UnsupportedFormatException {
- if (mDictBuffer == null) {
- openDictBuffer();
+ final DictionaryHeader header = readHeader();
+ final FusionDictionary fusionDict =
+ new FusionDictionary(new FusionDictionary.PtNodeArray(), header.mDictionaryOptions);
+ int token = 0;
+ final ArrayList<WordProperty> wordProperties = CollectionUtils.newArrayList();
+ do {
+ final BinaryDictionary.GetNextWordPropertyResult result =
+ mBinaryDictionary.getNextWordProperty(token);
+ final WordProperty wordProperty = result.mWordProperty;
+ if (wordProperty == null) {
+ if (deleteDictIfBroken) {
+ mBinaryDictionary.close();
+ mDictionaryBinaryFile.delete();
+ }
+ return null;
+ }
+ wordProperties.add(wordProperty);
+ token = result.mNextToken;
+ } while (token != 0);
+
+ // Insert unigrams into the fusion dictionary.
+ for (final WordProperty wordProperty : wordProperties) {
+ if (wordProperty.mIsBlacklistEntry) {
+ fusionDict.addBlacklistEntry(wordProperty.mWord, wordProperty.mShortcutTargets,
+ wordProperty.mIsNotAWord);
+ } else {
+ fusionDict.add(wordProperty.mWord, wordProperty.mProbabilityInfo,
+ wordProperty.mShortcutTargets, wordProperty.mIsNotAWord);
+ }
}
- try {
- return BinaryDictDecoderUtils.readDictionaryBinary(this);
- } catch (IOException e) {
- Log.e(TAG, "The dictionary " + mDictionaryBinaryFile.getName() + " is broken.", e);
- if (deleteDictIfBroken && !mDictionaryBinaryFile.delete()) {
- Log.e(TAG, "Failed to delete the broken dictionary.");
+ // Insert bigrams into the fusion dictionary.
+ for (final WordProperty wordProperty : wordProperties) {
+ if (wordProperty.mBigrams == null) {
+ continue;
}
- throw e;
- } catch (UnsupportedFormatException e) {
- Log.e(TAG, "The dictionary " + mDictionaryBinaryFile.getName() + " is broken.", e);
- if (deleteDictIfBroken && !mDictionaryBinaryFile.delete()) {
- Log.e(TAG, "Failed to delete the broken dictionary.");
+ final String word0 = wordProperty.mWord;
+ for (final WeightedString bigram : wordProperty.mBigrams) {
+ fusionDict.setBigram(word0, bigram.mWord, bigram.mProbabilityInfo);
}
- throw e;
}
+ return fusionDict;
}
@Override
@@ -272,19 +308,4 @@ public class Ver2DictDecoder extends AbstractDictDecoder {
public int readPtNodeCount() {
return BinaryDictDecoderUtils.readPtNodeCount(mDictBuffer);
}
-
- @Override
- public boolean readAndFollowForwardLink() {
- final int nextAddress = mDictBuffer.readUnsignedInt24();
- if (nextAddress >= 0 && nextAddress < mDictBuffer.limit()) {
- mDictBuffer.position(nextAddress);
- return true;
- }
- return false;
- }
-
- @Override
- public boolean hasNextPtNodeArray() {
- return mDictBuffer.position() != FormatSpec.NO_FORWARD_LINK_ADDRESS;
- }
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java
index 634f63137..e247f0121 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java
@@ -95,7 +95,7 @@ public class Ver2DictEncoder implements DictEncoder {
ArrayList<PtNodeArray> flatNodes = BinaryDictEncoderUtils.flattenTree(dict.mRootNodeArray);
MakedictLog.i("Computing addresses...");
- BinaryDictEncoderUtils.computeAddresses(dict, flatNodes, formatOptions);
+ BinaryDictEncoderUtils.computeAddresses(dict, flatNodes);
MakedictLog.i("Checking PtNode array...");
if (MakedictLog.DBG) BinaryDictEncoderUtils.checkFlatPtNodeArrayList(flatNodes);
@@ -107,7 +107,7 @@ public class Ver2DictEncoder implements DictEncoder {
MakedictLog.i("Writing file...");
for (PtNodeArray nodeArray : flatNodes) {
- BinaryDictEncoderUtils.writePlacedPtNodeArray(dict, this, nodeArray, formatOptions);
+ BinaryDictEncoderUtils.writePlacedPtNodeArray(dict, this, nodeArray);
}
if (MakedictLog.DBG) BinaryDictEncoderUtils.showStatistics(flatNodes);
mOutStream.write(mBuffer, 0, mPosition);
@@ -139,24 +139,13 @@ public class Ver2DictEncoder implements DictEncoder {
countSize);
}
- private void writePtNodeFlags(final PtNode ptNode, final FormatOptions formatOptions) {
- final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode, formatOptions);
+ private void writePtNodeFlags(final PtNode ptNode) {
+ final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode);
mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition,
- BinaryDictEncoderUtils.makePtNodeFlags(ptNode, childrenPos, formatOptions),
+ BinaryDictEncoderUtils.makePtNodeFlags(ptNode, childrenPos),
FormatSpec.PTNODE_FLAGS_SIZE);
}
- private void writeParentPosition(final int parentPosition, final PtNode ptNode,
- final FormatOptions formatOptions) {
- if (parentPosition == FormatSpec.NO_PARENT_ADDRESS) {
- mPosition = BinaryDictEncoderUtils.writeParentAddress(mBuffer, mPosition,
- parentPosition, formatOptions);
- } else {
- mPosition = BinaryDictEncoderUtils.writeParentAddress(mBuffer, mPosition,
- parentPosition - ptNode.mCachedAddressAfterUpdate, formatOptions);
- }
- }
-
private void writeCharacters(final int[] codePoints, final boolean hasSeveralChars) {
mPosition = CharEncoding.writeCharArray(codePoints, mBuffer, mPosition);
if (hasSeveralChars) {
@@ -171,15 +160,10 @@ public class Ver2DictEncoder implements DictEncoder {
}
}
- private void writeChildrenPosition(final PtNode ptNode, final FormatOptions formatOptions) {
- final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode, formatOptions);
- if (formatOptions.supportsDynamicUpdate()) {
- mPosition += BinaryDictEncoderUtils.writeSignedChildrenPosition(mBuffer, mPosition,
- childrenPos);
- } else {
- mPosition += BinaryDictEncoderUtils.writeChildrenPosition(mBuffer, mPosition,
- childrenPos);
- }
+ private void writeChildrenPosition(final PtNode ptNode) {
+ final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode);
+ mPosition += BinaryDictEncoderUtils.writeChildrenPosition(mBuffer, mPosition,
+ childrenPos);
}
/**
@@ -246,13 +230,11 @@ public class Ver2DictEncoder implements DictEncoder {
}
@Override
- public void writePtNode(final PtNode ptNode, final int parentPosition,
- final FormatOptions formatOptions, final FusionDictionary dict) {
- writePtNodeFlags(ptNode, formatOptions);
- writeParentPosition(parentPosition, ptNode, formatOptions);
+ public void writePtNode(final PtNode ptNode, final FusionDictionary dict) {
+ writePtNodeFlags(ptNode);
writeCharacters(ptNode.mChars, ptNode.hasSeveralChars());
writeFrequency(ptNode.getProbability());
- writeChildrenPosition(ptNode, formatOptions);
+ writeChildrenPosition(ptNode);
writeShortcuts(ptNode.mShortcutTargets);
writeBigrams(ptNode.mBigrams, dict);
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
index abaf96676..23aa05d18 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
@@ -45,6 +45,7 @@ public class Ver4DictDecoder extends AbstractDictDecoder {
@UsedForTesting
/* package */ Ver4DictDecoder(final File dictDirectory, final DictionaryBufferFactory factory) {
mDictDirectory = dictDirectory;
+ // dictType is not being used in dicttool. Passing an empty string.
mBinaryDictionary = new BinaryDictionary(dictDirectory.getAbsolutePath(),
0 /* offset */, 0 /* length */, true /* useFullEditDistance */, null /* locale */,
"" /* dictType */, true /* isUpdatable */);
@@ -78,7 +79,7 @@ public class Ver4DictDecoder extends AbstractDictDecoder {
token = result.mNextToken;
} while (token != 0);
- // Insert unigrams to the fusion dictionary.
+ // Insert unigrams into the fusion dictionary.
for (final WordProperty wordProperty : wordProperties) {
if (wordProperty.mIsBlacklistEntry) {
fusionDict.addBlacklistEntry(wordProperty.mWord, wordProperty.mShortcutTargets,
@@ -88,7 +89,7 @@ public class Ver4DictDecoder extends AbstractDictDecoder {
wordProperty.mShortcutTargets, wordProperty.mIsNotAWord);
}
}
- // Insert bigrams to the fusion dictionary.
+ // Insert bigrams into the fusion dictionary.
for (final WordProperty wordProperty : wordProperties) {
if (wordProperty.mBigrams == null) {
continue;
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
index 147844fd8..1050d1b0e 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
@@ -122,7 +122,6 @@ public class Ver4DictEncoder implements DictEncoder {
}
@Override
- public void writePtNode(
- PtNode ptNode, int parentPosition, FormatOptions formatOptions, FusionDictionary dict) {
+ public void writePtNode(PtNode ptNode, FusionDictionary dict) {
}
}
diff --git a/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java b/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java
index 4bf524cbb..6dae6206c 100644
--- a/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java
+++ b/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java
@@ -16,8 +16,6 @@
package com.android.inputmethod.latin.settings;
-import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.ASCII_CAPABLE;
-
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
@@ -44,6 +42,8 @@ import android.widget.Spinner;
import android.widget.SpinnerAdapter;
import android.widget.Toast;
+import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
+import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.RichInputMethodManager;
import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
@@ -111,7 +111,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment {
subtype.getLocale(), subtype.hashCode(), subtype.hashCode(),
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype)));
}
- if (subtype.containsExtraValueKey(ASCII_CAPABLE)) {
+ if (InputMethodSubtypeCompatUtils.isAsciiCapable(subtype)) {
items.add(createItem(context, subtype.getLocale()));
}
}
@@ -287,7 +287,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment {
final KeyboardLayoutSetItem layout =
(KeyboardLayoutSetItem) mKeyboardLayoutSetSpinner.getSelectedItem();
final InputMethodSubtype subtype = AdditionalSubtypeUtils.createAdditionalSubtype(
- locale.first, layout.first, ASCII_CAPABLE);
+ locale.first, layout.first, Constants.Subtype.ExtraValue.ASCII_CAPABLE);
setSubtype(subtype);
notifyChanged();
if (isEditing) {
diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java
index 45c5b733f..b51c765f0 100644
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ b/java/src/com/android/inputmethod/latin/settings/Settings.java
@@ -66,6 +66,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
"pref_include_other_imes_in_language_switch_list";
public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20110916";
public static final String PREF_CUSTOM_INPUT_STYLES = "custom_input_styles";
+ // TODO: consolidate key preview dismiss delay with the key preview animation parameters.
public static final String PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY =
"pref_key_preview_popup_dismiss_delay";
public static final String PREF_BIGRAM_PREDICTIONS = "next_word_prediction";
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
index 67017a43b..bb5547fc9 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
@@ -169,6 +169,7 @@ public final class SettingsFragment extends InputMethodSettingsFragment
removePreference(Settings.PREF_VIBRATION_DURATION_SETTINGS, advancedSettings);
}
+ // TODO: consolidate key preview dismiss delay with the key preview animation parameters.
if (!Settings.readFromBuildConfigIfToShowKeyPreviewPopupOption(res)) {
removePreference(Settings.PREF_POPUP_ON, generalSettings);
removePreference(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY, advancedSettings);
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
index 7c11f5432..e77c55069 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
@@ -44,7 +44,6 @@ import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
-import com.android.inputmethod.compat.TextViewCompatUtils;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.PunctuationSuggestions;
import com.android.inputmethod.latin.R;
@@ -506,15 +505,10 @@ final class SuggestionStripLayoutHelper {
}
public void layoutImportantNotice(final View importantNoticeStrip, final int stripWidth) {
- final Resources res = importantNoticeStrip.getResources();
- final Drawable infoIcon = res.getDrawable(R.drawable.sym_keyboard_info_holo_dark);
- final Drawable moreIcon = res.getDrawable(R.drawable.sym_keyboard_more_holo_dark);
- final int width = stripWidth - infoIcon.getIntrinsicWidth() - moreIcon.getIntrinsicWidth();
final TextView titleView = (TextView)importantNoticeStrip.findViewById(
R.id.important_notice_title);
+ final int width = stripWidth - titleView.getPaddingLeft() - titleView.getPaddingRight();
titleView.setTextColor(mColorAutoCorrect);
- TextViewCompatUtils.setCompoundDrawablesRelativeWithIntrinsicBounds(
- titleView, infoIcon, null, moreIcon, null);
final CharSequence importantNoticeTitle = titleView.getText();
titleView.setTextScaleX(1.0f); // Reset textScaleX.
final float titleScaleX = getTextScaleX(
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index cf0a7a2aa..1f80c4cca 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -39,11 +39,13 @@ import com.android.inputmethod.keyboard.MainKeyboardView;
import com.android.inputmethod.keyboard.MoreKeysPanel;
import com.android.inputmethod.latin.AudioAndHapticFeedbackManager;
import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.InputAttributes;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.latin.settings.Settings;
import com.android.inputmethod.latin.suggestions.MoreSuggestions.MoreSuggestionsListener;
import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.ImportantNoticeUtils;
@@ -226,12 +228,14 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
// This method checks if we should show the important notice (checks on permanent storage if
// it has been shown once already or not, and if in the setup wizard). If applicable, it shows
// the notice. In all cases, it returns true if it was shown, false otherwise.
- public boolean maybeShowImportantNoticeTitle() {
- if (!ImportantNoticeUtils.hasNewImportantNoticeAndNotInSetupWizard(getContext())) {
+ public boolean maybeShowImportantNoticeTitle(final InputAttributes inputAttributes) {
+ if (!ImportantNoticeUtils.shouldShowImportantNotice(getContext(), inputAttributes)) {
return false;
}
final int width = getWidth();
- if (width <= 0) return false;
+ if (width <= 0) {
+ return false;
+ }
mLayoutHelper.layoutImportantNotice(mImportantNoticeStrip, width);
mStripVisibilityGroup.showImportantNoticeStrip();
mImportantNoticeStrip.setOnClickListener(this);
@@ -325,7 +329,6 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
final int pointY = -layoutHelper.mMoreSuggestionsBottomGap;
moreKeysPanel.showMoreKeysPanel(this, mMoreSuggestionsController, pointX, pointY,
mMoreSuggestionsListener);
- mMoreSuggestionsMode = MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING;
mOriginX = mLastX;
mOriginY = mLastY;
for (int i = 0; i < mSuggestionsCountInStrip; i++) {
@@ -334,11 +337,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
return true;
}
- // Working variables for onLongClick and dispatchTouchEvent.
- private int mMoreSuggestionsMode = MORE_SUGGESTIONS_IN_MODAL_MODE;
- private static final int MORE_SUGGESTIONS_IN_MODAL_MODE = 0;
- private static final int MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING = 1;
- private static final int MORE_SUGGESTIONS_IN_SLIDING_MODE = 2;
+ // Working variables for {@link #onLongClick(View)} and
+ // {@link onInterceptTouchEvent(MotionEvent)}.
private int mLastX;
private int mLastY;
private int mOriginX;
@@ -358,36 +358,39 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
};
@Override
- public boolean dispatchTouchEvent(final MotionEvent me) {
+ public boolean onInterceptTouchEvent(final MotionEvent me) {
if (!mMoreSuggestionsView.isShowingInParent()) {
mLastX = (int)me.getX();
mLastY = (int)me.getY();
- if (mMoreSuggestionsSlidingDetector.onTouchEvent(me)) {
- return true;
- }
- return super.dispatchTouchEvent(me);
+ return mMoreSuggestionsSlidingDetector.onTouchEvent(me);
}
final int action = me.getAction();
final int index = me.getActionIndex();
final int x = (int)me.getX(index);
final int y = (int)me.getY(index);
-
- if (mMoreSuggestionsMode == MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING) {
- if (Math.abs(x - mOriginX) >= mMoreSuggestionsModalTolerance
- || mOriginY - y >= mMoreSuggestionsModalTolerance) {
- // Decided to be in the sliding input mode only when the touch point has been moved
- // upward.
- mMoreSuggestionsMode = MORE_SUGGESTIONS_IN_SLIDING_MODE;
- } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP) {
- // Decided to be in the modal input mode
- mMoreSuggestionsMode = MORE_SUGGESTIONS_IN_MODAL_MODE;
- mMoreSuggestionsView.adjustVerticalCorrectionForModalMode();
- }
+ if (Math.abs(x - mOriginX) >= mMoreSuggestionsModalTolerance
+ || mOriginY - y >= mMoreSuggestionsModalTolerance) {
+ // Decided to be in the sliding input mode only when the touch point has been moved
+ // upward. Further {@link MotionEvent}s will be delivered to
+ // {@link #onTouchEvent(MotionEvent)}.
return true;
}
- // MORE_SUGGESTIONS_IN_SLIDING_MODE
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP) {
+ // Decided to be in the modal input mode.
+ mMoreSuggestionsView.adjustVerticalCorrectionForModalMode();
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onTouchEvent(final MotionEvent me) {
+ // In the sliding input mode. {@link MotionEvent} should be forwarded to
+ // {@link MoreSuggestionsView}.
+ final int index = me.getActionIndex();
+ final int x = (int)me.getX(index);
+ final int y = (int)me.getY(index);
me.setLocation(mMoreSuggestionsView.translateX(x), mMoreSuggestionsView.translateY(y));
mMoreSuggestionsView.onTouchEvent(me);
return true;
@@ -428,9 +431,11 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
}
@Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ protected void onSizeChanged(final int w, final int h, final int oldw, final int oldh) {
// Called by the framework when the size is known. Show the important notice if applicable.
// This may be overriden by showing suggestions later, if applicable.
- maybeShowImportantNoticeTitle();
+ if (oldw <= 0 && w > 0) {
+ maybeShowImportantNoticeTitle(Settings.getInstance().getCurrent().mInputAttributes);
+ }
}
}
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java
index 32c4950da..2f41ce9ce 100644
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java
@@ -61,12 +61,17 @@ public class UserDictionaryList extends PreferenceFragment {
if (null == cursor) {
// The user dictionary service is not present or disabled. Return null.
return null;
- } else if (cursor.moveToFirst()) {
- final int columnIndex = cursor.getColumnIndex(UserDictionary.Words.LOCALE);
- do {
- final String locale = cursor.getString(columnIndex);
- localeSet.add(null != locale ? locale : "");
- } while (cursor.moveToNext());
+ }
+ try {
+ if (cursor.moveToFirst()) {
+ final int columnIndex = cursor.getColumnIndex(UserDictionary.Words.LOCALE);
+ do {
+ final String locale = cursor.getString(columnIndex);
+ localeSet.add(null != locale ? locale : "");
+ } while (cursor.moveToNext());
+ }
+ } finally {
+ cursor.close();
}
if (!UserDictionarySettings.IS_SHORTCUT_API_SUPPORTED) {
// For ICS, we need to show "For all languages" in case that the keyboard locale
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java
index 7571e87c5..220efb5d3 100644
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java
@@ -140,6 +140,8 @@ public class UserDictionarySettings extends ListFragment {
}
mLocale = locale;
+ // WARNING: The following cursor is never closed! TODO: don't put that in a member, and
+ // make sure all cursors are correctly closed.
mCursor = createCursor(locale);
TextView emptyView = (TextView) getView().findViewById(android.R.id.empty);
emptyView.setText(R.string.user_dict_settings_empty_text);
diff --git a/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java b/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java
index 604c36488..50a942382 100644
--- a/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java
@@ -22,6 +22,7 @@ import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.Log;
+import com.android.inputmethod.latin.InputAttributes;
import com.android.inputmethod.latin.R;
public final class ImportantNoticeUtils {
@@ -62,11 +63,18 @@ public final class ImportantNoticeUtils {
return context.getResources().getInteger(R.integer.config_important_notice_version);
}
- public static boolean hasNewImportantNoticeAndNotInSetupWizard(final Context context) {
+ private static boolean hasNewImportantNotice(final Context context) {
final SharedPreferences prefs = getImportantNoticePreferences(context);
final int lastVersion = prefs.getInt(KEY_IMPORTANT_NOTICE_VERSION, 0);
- return getCurrentImportantNoticeVersion(context) > lastVersion
- && !isInSystemSetupWizard(context);
+ return getCurrentImportantNoticeVersion(context) > lastVersion;
+ }
+
+ public static boolean shouldShowImportantNotice(final Context context,
+ final InputAttributes inputAttributes) {
+ if (inputAttributes == null || inputAttributes.mIsPasswordField) {
+ return false;
+ }
+ return hasNewImportantNotice(context) && !isInSystemSetupWizard(context);
}
public static void updateLastImportantNoticeVersion(final Context context) {
diff --git a/java/src/com/android/inputmethod/latin/utils/SpannableStringUtils.java b/java/src/com/android/inputmethod/latin/utils/SpannableStringUtils.java
index be0955456..38164cb36 100644
--- a/java/src/com/android/inputmethod/latin/utils/SpannableStringUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/SpannableStringUtils.java
@@ -22,6 +22,7 @@ import android.text.Spanned;
import android.text.SpannedString;
import android.text.TextUtils;
import android.text.style.SuggestionSpan;
+import android.text.style.URLSpan;
public final class SpannableStringUtils {
/**
@@ -112,4 +113,16 @@ public final class SpannableStringUtils {
return new SpannedString(ss);
}
+
+ public static boolean hasUrlSpans(final CharSequence text,
+ final int startIndex, final int endIndex) {
+ if (!(text instanceof Spanned)) {
+ return false; // Not spanned, so no link
+ }
+ final Spanned spanned = (Spanned)text;
+ // getSpans(x, y) does not return spans that start on x or end on y. x-1, y+1 does the
+ // trick, and works in all cases even if startIndex <= 0 or endIndex >= text.length().
+ final URLSpan[] spans = spanned.getSpans(startIndex - 1, endIndex + 1, URLSpan.class);
+ return null != spans && spans.length > 0;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/utils/TextRange.java b/java/src/com/android/inputmethod/latin/utils/TextRange.java
index 48b443ddd..dbf3b5060 100644
--- a/java/src/com/android/inputmethod/latin/utils/TextRange.java
+++ b/java/src/com/android/inputmethod/latin/utils/TextRange.java
@@ -31,6 +31,7 @@ public final class TextRange {
private final int mCursorIndex;
public final CharSequence mWord;
+ public final boolean mHasUrlSpans;
public int getNumberOfCharsInWordBeforeCursor() {
return mCursorIndex - mWordAtCursorStartIndex;
@@ -95,7 +96,7 @@ public final class TextRange {
}
}
if (spanStart == mWordAtCursorStartIndex && spanEnd == mWordAtCursorEndIndex) {
- // If the span does not start and stop here, we ignore it. It probably extends
+ // If the span does not start and stop here, ignore it. It probably extends
// past the start or end of the word, as happens in missing space correction
// or EasyEditSpans put by voice input.
spans[writeIndex++] = spans[readIndex];
@@ -105,7 +106,7 @@ public final class TextRange {
}
public TextRange(final CharSequence textAtCursor, final int wordAtCursorStartIndex,
- final int wordAtCursorEndIndex, final int cursorIndex) {
+ final int wordAtCursorEndIndex, final int cursorIndex, final boolean hasUrlSpans) {
if (wordAtCursorStartIndex < 0 || cursorIndex < wordAtCursorStartIndex
|| cursorIndex > wordAtCursorEndIndex
|| wordAtCursorEndIndex > textAtCursor.length()) {
@@ -115,6 +116,7 @@ public final class TextRange {
mWordAtCursorStartIndex = wordAtCursorStartIndex;
mWordAtCursorEndIndex = wordAtCursorEndIndex;
mCursorIndex = cursorIndex;
+ mHasUrlSpans = hasUrlSpans;
mWord = mTextAtCursor.subSequence(mWordAtCursorStartIndex, mWordAtCursorEndIndex);
}
} \ No newline at end of file