diff options
Diffstat (limited to 'java/src/com/android/inputmethod')
25 files changed, 625 insertions, 440 deletions
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 index e95ca1799..75cc7d4cb 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DownloadManagerWrapper.java +++ b/java/src/com/android/inputmethod/dictionarypack/DownloadManagerWrapper.java @@ -59,6 +59,10 @@ public class DownloadManagerWrapper { // 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); } } @@ -69,6 +73,8 @@ public class DownloadManagerWrapper { } } 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(); @@ -81,6 +87,8 @@ public class DownloadManagerWrapper { } } 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; @@ -93,6 +101,8 @@ public class DownloadManagerWrapper { } } 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/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java index 282c8e8fa..03d9defa0 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java +++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java @@ -16,8 +16,6 @@ package com.android.inputmethod.keyboard; -import com.android.inputmethod.latin.Constants; - /** * This class handles key detection. */ @@ -41,13 +39,15 @@ public class KeyDetector { * @param keyHysteresisDistanceForSlidingModifier the same parameter for sliding input that * starts from a modifier key such as shift and symbols key. */ - public KeyDetector(float keyHysteresisDistance, float keyHysteresisDistanceForSlidingModifier) { + public KeyDetector(final float keyHysteresisDistance, + final float keyHysteresisDistanceForSlidingModifier) { mKeyHysteresisDistanceSquared = (int)(keyHysteresisDistance * keyHysteresisDistance); mKeyHysteresisDistanceForSlidingModifierSquared = (int)( keyHysteresisDistanceForSlidingModifier * keyHysteresisDistanceForSlidingModifier); } - public void setKeyboard(Keyboard keyboard, float correctionX, float correctionY) { + public void setKeyboard(final Keyboard keyboard, final float correctionX, + final float correctionY) { if (keyboard == null) { throw new NullPointerException(); } @@ -56,24 +56,21 @@ public class KeyDetector { mKeyboard = keyboard; } - public int getKeyHysteresisDistanceSquared(boolean isSlidingFromModifier) { + public int getKeyHysteresisDistanceSquared(final boolean isSlidingFromModifier) { return isSlidingFromModifier ? mKeyHysteresisDistanceForSlidingModifierSquared : mKeyHysteresisDistanceSquared; } - public int getTouchX(int x) { + public int getTouchX(final int x) { return x + mCorrectionX; } // TODO: Remove vertical correction. - public int getTouchY(int y) { + public int getTouchY(final int y) { return y + mCorrectionY; } public Keyboard getKeyboard() { - if (mKeyboard == null) { - throw new IllegalStateException("keyboard isn't set"); - } return mKeyboard; } @@ -88,7 +85,7 @@ public class KeyDetector { * @param y The y-coordinate of a touch point * @return the key that the touch point hits. */ - public Key detectHitKey(int x, int y) { + public Key detectHitKey(final int x, final int y) { final int touchX = getTouchX(x); final int touchY = getTouchY(y); @@ -113,20 +110,4 @@ public class KeyDetector { } return primaryKey; } - - public static String printableCode(Key key) { - return key != null ? Constants.printableCode(key.getCode()) : "none"; - } - - public static String printableCodes(int[] codes) { - final StringBuilder sb = new StringBuilder(); - boolean addDelimiter = false; - for (final int code : codes) { - if (code == Constants.NOT_A_CODE) break; - if (addDelimiter) sb.append(", "); - sb.append(Constants.printableCode(code)); - addDelimiter = true; - } - return "[" + sb + "]"; - } } diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java index 4a80279ca..abff202b7 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java @@ -33,13 +33,17 @@ public final class MoreKeysDetector extends KeyDetector { } @Override - public Key detectHitKey(int x, int y) { + public Key detectHitKey(final int x, final int y) { + final Keyboard keyboard = getKeyboard(); + if (keyboard == null) { + return null; + } final int touchX = getTouchX(x); final int touchY = getTouchY(y); Key nearestKey = null; int nearestDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare; - for (final Key key : getKeyboard().getKeys()) { + for (final Key key : keyboard.getKeys()) { final int dist = key.squaredDistanceToEdge(touchX, touchY); if (dist < nearestDist) { nearestKey = key; diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index befc4e6fa..59cf64d4b 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -257,12 +257,15 @@ public final class PointerTracker implements PointerTrackerQueue.Element, } public static void setKeyDetector(final KeyDetector keyDetector) { + final Keyboard keyboard = keyDetector.getKeyboard(); + if (keyboard == null) { + return; + } final int trackersSize = sTrackers.size(); for (int i = 0; i < trackersSize; ++i) { final PointerTracker tracker = sTrackers.get(i); tracker.setKeyDetectorInner(keyDetector); } - final Keyboard keyboard = keyDetector.getKeyboard(); sGestureEnabler.setPasswordMode(keyboard.mId.passwordInput()); } @@ -301,7 +304,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element, final boolean ignoreModifierKey = mIsInDraggingFinger && key.isModifier(); if (DEBUG_LISTENER) { Log.d(TAG, String.format("[%d] onPress : %s%s%s%s", mPointerId, - KeyDetector.printableCode(key), + (key == null ? "none" : Constants.printableCode(key.getCode())), ignoreModifierKey ? " ignoreModifier" : "", key.isEnabled() ? "" : " disabled", repeatCount > 0 ? " repeatCount=" + repeatCount : "")); @@ -402,11 +405,14 @@ public final class PointerTracker implements PointerTrackerQueue.Element, private void setKeyDetectorInner(final KeyDetector keyDetector) { final Keyboard keyboard = keyDetector.getKeyboard(); + if (keyboard == null) { + return; + } if (keyDetector == mKeyDetector && keyboard == mKeyboard) { return; } mKeyDetector = keyDetector; - mKeyboard = keyDetector.getKeyboard(); + mKeyboard = keyboard; // Mark that keyboard layout has been changed. mKeyboardLayoutHasBeenChanged = true; final int keyWidth = mKeyboard.mMostCommonKeyWidth; @@ -1235,7 +1241,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element, private void printTouchEvent(final String title, final int x, final int y, final long eventTime) { final Key key = mKeyDetector.detectHitKey(x, y); - final String code = KeyDetector.printableCode(key); + final String code = (key == null ? "none" : Constants.printableCode(key.getCode())); Log.d(TAG, String.format("[%d]%s%s %4d %4d %5d %s", mPointerId, (mIsTrackingForActionDisabled ? "-" : " "), title, x, y, eventTime, code)); } 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/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java index d6ac71fe2..d1ff714fc 100644 --- a/java/src/com/android/inputmethod/latin/Constants.java +++ b/java/src/com/android/inputmethod/latin/Constants.java @@ -252,6 +252,18 @@ public final class Constants { } } + public static String printableCodes(final int[] codes) { + final StringBuilder sb = new StringBuilder(); + boolean addDelimiter = false; + for (final int code : codes) { + if (code == NOT_A_CODE) break; + if (addDelimiter) sb.append(", "); + sb.append(printableCode(code)); + addDelimiter = true; + } + return "[" + sb + "]"; + } + public static final int MAX_INT_BIT_COUNT = 32; /** 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/DictionaryFacilitatorForSuggest.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java index 259c1372e..138a626a0 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java @@ -512,7 +512,6 @@ public class DictionaryFacilitatorForSuggest { } } - @UsedForTesting public void clearUserHistoryDictionary() { if (mUserHistoryDictionary == null) { return; diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java index 01c17f2f2..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. */ @@ -62,49 +66,43 @@ public final class InputAttributes { 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); - - mIsPasswordField = InputTypeUtils.isPasswordInputType(inputType) - || InputTypeUtils.isVisiblePasswordInputType(inputType); - // TODO: Have a helper method in InputTypeUtils - // Make sure that passwords are not displayed in {@link SuggestionStripView}. - if (mIsPasswordField - || 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() { @@ -117,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/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 743118eed..5d90e10a0 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -25,6 +25,9 @@ import android.app.AlertDialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.DialogInterface.OnDismissListener; +import android.content.DialogInterface.OnShowListener; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; @@ -171,10 +174,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 +340,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(); } @@ -530,18 +534,33 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private void refreshPersonalizationDictionarySession() { + final Suggest suggest = mInputLogic.mSuggest; + final boolean shouldKeepUserHistoryDictionaries; + final boolean shouldKeepPersonalizationDictionaries; if (mSettings.getCurrent().mUsePersonalizedDicts) { - if (mSubtypeSwitcher.isSystemLocaleSameAsLocaleOfAllEnabledSubtypes()) { - final DictionaryFacilitatorForSuggest dictionaryFacilitator = - (mInputLogic.mSuggest == null) ? - null : mInputLogic.mSuggest.mDictionaryFacilitator; - PersonalizationDictionarySessionRegistrar.init(this, dictionaryFacilitator); - } else { - PersonalizationDictionarySessionRegistrar.close(this); - } + shouldKeepUserHistoryDictionaries = true; + // TODO: Eliminate this restriction + shouldKeepPersonalizationDictionaries = + mSubtypeSwitcher.isSystemLocaleSameAsLocaleOfAllEnabledSubtypes(); } else { - PersonalizationHelper.removeAllPersonalizedDictionaries(this); + shouldKeepUserHistoryDictionaries = false; + shouldKeepPersonalizationDictionaries = false; + } + if (!shouldKeepUserHistoryDictionaries) { + // Remove user history dictionaries. + PersonalizationHelper.removeAllUserHistoryDictionaries(this); + if (suggest != null) { + suggest.mDictionaryFacilitator.clearUserHistoryDictionary(); + } + } + if (!shouldKeepPersonalizationDictionaries) { + // Remove personalization dictionaries. + PersonalizationHelper.removeAllPersonalizationDictionaries(this); PersonalizationDictionarySessionRegistrar.resetAll(this); + } else { + final DictionaryFacilitatorForSuggest dictionaryFacilitator = + (suggest == null) ? null : suggest.mDictionaryFacilitator; + PersonalizationDictionarySessionRegistrar.init(this, dictionaryFacilitator); } } @@ -579,9 +598,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 +676,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 +756,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"); } @@ -777,14 +793,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Note: the following does a round-trip IPC on the main thread: be careful final Locale currentLocale = mSubtypeSwitcher.getCurrentSubtypeLocale(); - final Suggest suggest = mInputLogic.mSuggest; + Suggest suggest = mInputLogic.mSuggest; 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(); + suggest = mInputLogic.mSuggest; } // Sometimes, while rotating, for some reason the framework tells the app we are not @@ -809,6 +821,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (isDifferentTextField || !currentSettingsValues.hasSameOrientation(getResources().getConfiguration())) { loadSettings(); + suggest = mInputLogic.mSuggest; } if (isDifferentTextField) { mainKeyboardView.closing(); @@ -834,8 +847,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 +908,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 +994,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 +1010,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 +1068,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(); @@ -1172,31 +1183,54 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void showImportantNoticeContents() { final Context context = this; - final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { + final OnShowListener onShowListener = new OnShowListener() { @Override - public void onClick(final DialogInterface di, final int position) { - di.dismiss(); + public void onShow(final DialogInterface dialog) { ImportantNoticeUtils.updateLastImportantNoticeVersion(context); - if (position == DialogInterface.BUTTON_POSITIVE) { - setNeutralSuggestionStrip(); - return; - } + onShowImportantNoticeDialog( + ImportantNoticeUtils.getCurrentImportantNoticeVersion(context)); + } + }; + final OnClickListener onClickListener = new OnClickListener() { + @Override + public void onClick(final DialogInterface dialog, final int position) { 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) - .setNegativeButton(R.string.go_to_settings, listener); - showOptionDialog(builder.create(), true /* cancelable */); + final OnDismissListener onDismissListener = new OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + setNeutralSuggestionStrip(); + } + }; + final String importantNoticeContents = ImportantNoticeUtils.getImportantNoticeContents( + context); + final AlertDialog.Builder builder = new AlertDialog.Builder( + context, AlertDialog.THEME_HOLO_DARK); + builder.setMessage(importantNoticeContents) + .setPositiveButton(android.R.string.ok, null /* listener */) + .setNegativeButton(R.string.go_to_settings, onClickListener); + final AlertDialog importantNoticeDialog = builder.create(); + importantNoticeDialog.setOnShowListener(onShowListener); + importantNoticeDialog.setOnDismissListener(onDismissListener); + showOptionDialog(importantNoticeDialog); + } + + private void onShowImportantNoticeDialog(final int importantNoticeVersion) { + if (importantNoticeVersion == + ImportantNoticeUtils.VERSION_TO_ENABLE_PERSONALIZED_SUGGESTIONS) { + mSettings.writeUsePersonalizationDictionary(true /* enabled */); + loadSettings(); + initSuggest(); + } } public void displaySettingsDialog() { - if (isShowingOptionDialog()) return; + if (isShowingOptionDialog()) { + return; + } showSubtypeSelectorAndSettings(); } @@ -1256,8 +1290,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); } @@ -1314,27 +1348,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()) + } + if (mSuggestionStripView.isShowingAddToDictionaryHint()) { return true; - if (null == currentSettings) + } + final SettingsValues currentSettings = mSettings.getCurrent(); + if (null == currentSettings) { return false; - if (ImportantNoticeUtils.shouldShowImportantNotice(this, currentSettings.mInputAttributes)) + } + if (ImportantNoticeUtils.shouldShowImportantNotice(this, + currentSettings.mInputAttributes)) { return true; - if (!currentSettings.isSuggestionStripVisible()) + } + if (!currentSettings.isSuggestionStripVisible()) { return false; - if (currentSettings.isApplicationSpecifiedCompletionsOn()) + } + if (currentSettings.isApplicationSpecifiedCompletionsOn()) { return true; + } return currentSettings.isSuggestionsRequested(); } @@ -1350,32 +1385,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 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(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. @@ -1414,9 +1452,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 @@ -1430,7 +1468,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 = @@ -1440,10 +1478,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 */); } } @@ -1463,7 +1499,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. @@ -1481,7 +1518,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); } @@ -1490,13 +1529,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 @@ -1638,7 +1678,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen getString(R.string.language_selection_title), getString(ApplicationUtils.getActivityTitleResId(this, SettingsActivity.class)), }; - final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { + final OnClickListener listener = new OnClickListener() { @Override public void onClick(DialogInterface di, int position) { di.dismiss(); @@ -1659,18 +1699,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen }; final AlertDialog.Builder builder = new AlertDialog.Builder(this).setItems(items, listener).setTitle(title); - showOptionDialog(builder.create(), true /*cancelable */); + showOptionDialog(builder.create()); } // TODO: Move this method out of {@link LatinIME}. - private void showOptionDialog(final AlertDialog dialog, final boolean cancelable) { + private void showOptionDialog(final AlertDialog dialog) { final IBinder windowToken = mKeyboardSwitcher.getMainKeyboardView().getWindowToken(); if (windowToken == null) { return; } - dialog.setCancelable(cancelable); - dialog.setCanceledOnTouchOutside(cancelable); + dialog.setCancelable(true /* cancelable */); + dialog.setCanceledOnTouchOutside(true /* cancelable */); final Window window = dialog.getWindow(); final WindowManager.LayoutParams lp = window.getAttributes(); @@ -1685,7 +1725,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 eb1899ca2..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) { diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index 9bf9d1f45..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; } } @@ -1456,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/Ver2DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java index 315bd8e58..71e120c5f 100644 --- a/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java @@ -173,6 +173,9 @@ public class Ver2DictDecoder extends AbstractDictDecoder { @Override public DictionaryHeader readHeader() throws IOException, UnsupportedFormatException { final DictionaryHeader header = mBinaryDictionary.getHeader(); + if (header == null) { + throw new IOException("Cannot read the dictionary header."); + } if (header.mFormatOptions.mVersion != FormatSpec.VERSION2) { throw new UnsupportedFormatException("File header has a wrong version : " + header.mFormatOptions.mVersion); diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java index df64bcec1..5ae2fb6f8 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java @@ -93,13 +93,16 @@ public class PersonalizationHelper { } } - public static void removeAllPersonalizedDictionaries(final Context context) { - removeAllDictionaries(context, sLangUserHistoryDictCache, - UserHistoryDictionary.NAME); + public static void removeAllPersonalizationDictionaries(final Context context) { removeAllDictionaries(context, sLangPersonalizationDictCache, PersonalizationDictionary.NAME); } + public static void removeAllUserHistoryDictionaries(final Context context) { + removeAllDictionaries(context, sLangUserHistoryDictCache, + UserHistoryDictionary.NAME); + } + private static <T extends DecayingExpandableBinaryDictionaryBase> void removeAllDictionaries( final Context context, final ConcurrentHashMap<String, SoftReference<T>> dictionaryMap, final String dictNamePrefix) { diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java index b51c765f0..6a1503fed 100644 --- a/java/src/com/android/inputmethod/latin/settings/Settings.java +++ b/java/src/com/android/inputmethod/latin/settings/Settings.java @@ -55,7 +55,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final String PREF_MISC_SETTINGS = "misc_settings"; public static final String PREF_ADVANCED_SETTINGS = "pref_advanced_settings"; public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict"; - public static final String PREF_KEY_USE_PERSONALIZED_DICTS = "pref_key_use_personalized_dicts"; + public static final String PREF_USE_PERSONALIZED_DICTS = "pref_use_personalized_dicts"; public static final String PREF_KEY_USE_DOUBLE_SPACE_PERIOD = "pref_key_use_double_space_period"; public static final String PREF_BLOCK_POTENTIALLY_OFFENSIVE = @@ -421,6 +421,10 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang return mPrefs.getStringSet(PREF_CORPUS_HANDLES_FOR_PERSONALIZATION, emptySet); } + public void writeUsePersonalizationDictionary(final boolean enabled) { + mPrefs.edit().putBoolean(PREF_USE_PERSONALIZED_DICTS, enabled).apply(); + } + public static void writeEmojiRecentKeys(final SharedPreferences prefs, String str) { prefs.edit().putString(PREF_EMOJI_RECENT_KEYS, str).apply(); } diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index 77968f79a..0f3deeaa9 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -128,7 +128,7 @@ public final class SettingsValues { Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST, false); mShowsLanguageSwitchKey = Settings.readShowsLanguageSwitchKey(prefs); mUseContactsDict = prefs.getBoolean(Settings.PREF_KEY_USE_CONTACTS_DICT, true); - mUsePersonalizedDicts = prefs.getBoolean(Settings.PREF_KEY_USE_PERSONALIZED_DICTS, true); + mUsePersonalizedDicts = prefs.getBoolean(Settings.PREF_USE_PERSONALIZED_DICTS, false); mUseDoubleSpacePeriod = prefs.getBoolean(Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, true); mBlockPotentiallyOffensive = Settings.readBlockPotentiallyOffensive(prefs, res); mAutoCorrectEnabled = Settings.readAutoCorrectEnabled(autoCorrectionThresholdRawValue, res); diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java index e77c55069..8ea712835 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java @@ -504,12 +504,13 @@ final class SuggestionStripLayoutHelper { hintView, 1.0f - mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT); } - public void layoutImportantNotice(final View importantNoticeStrip, final int stripWidth) { + public void layoutImportantNotice(final View importantNoticeStrip, final int stripWidth, + final String importantNoticeTitle) { final TextView titleView = (TextView)importantNoticeStrip.findViewById( R.id.important_notice_title); final int width = stripWidth - titleView.getPaddingLeft() - titleView.getPaddingRight(); titleView.setTextColor(mColorAutoCorrect); - final CharSequence importantNoticeTitle = titleView.getText(); + titleView.setText(importantNoticeTitle); titleView.setTextScaleX(1.0f); // Reset textScaleX. final float titleScaleX = getTextScaleX( importantNoticeTitle, width, titleView.getPaint()); diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index 90b9692c3..2966a8bba 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Color; import android.support.v4.view.ViewCompat; +import android.text.TextUtils; import android.util.AttributeSet; import android.util.TypedValue; import android.view.GestureDetector; @@ -233,8 +234,15 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick return false; } final int width = getWidth(); - if (width <= 0) return false; - mLayoutHelper.layoutImportantNotice(mImportantNoticeStrip, width); + if (width <= 0) { + return false; + } + final String importantNoticeTitle = ImportantNoticeUtils.getImportantNoticeTitle( + getContext()); + if (TextUtils.isEmpty(importantNoticeTitle)) { + return false; + } + mLayoutHelper.layoutImportantNotice(mImportantNoticeStrip, width, importantNoticeTitle); mStripVisibilityGroup.showImportantNoticeStrip(); mImportantNoticeStrip.setOnClickListener(this); return true; @@ -429,9 +437,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(Settings.getInstance().getCurrent().mInputAttributes); + 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 50a942382..dd418b8cf 100644 --- a/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java @@ -32,6 +32,7 @@ public final class ImportantNoticeUtils { // displayed to users. private static final String PREFERENCE_NAME = "important_notice"; private static final String KEY_IMPORTANT_NOTICE_VERSION = "important_notice_version"; + public static final int VERSION_TO_ENABLE_PERSONALIZED_SUGGESTIONS = 2; // Copy of the hidden {@link Settings.Secure#USER_SETUP_COMPLETE} settings key. // The value is zero until each multiuser completes system setup wizard. @@ -59,7 +60,7 @@ public final class ImportantNoticeUtils { return context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); } - private static int getCurrentImportantNoticeVersion(final Context context) { + public static int getCurrentImportantNoticeVersion(final Context context) { return context.getResources().getInteger(R.integer.config_important_notice_version); } @@ -83,4 +84,22 @@ public final class ImportantNoticeUtils { .putInt(KEY_IMPORTANT_NOTICE_VERSION, getCurrentImportantNoticeVersion(context)) .apply(); } + + public static String getImportantNoticeTitle(final Context context) { + switch (getCurrentImportantNoticeVersion(context)) { + case VERSION_TO_ENABLE_PERSONALIZED_SUGGESTIONS: + return context.getString(R.string.important_notice_title); + default: + return null; + } + } + + public static String getImportantNoticeContents(final Context context) { + switch (getCurrentImportantNoticeVersion(context)) { + case VERSION_TO_ENABLE_PERSONALIZED_SUGGESTIONS: + return context.getString(R.string.important_notice_contents); + default: + return null; + } + } } |