diff --git a/src/com/android/inputmethod/latin/ContactsDictionary.java b/src/com/android/inputmethod/latin/ContactsDictionary.java new file mode 100644 index 000000000..756444302 --- /dev/null +++ b/src/com/android/inputmethod/latin/ContactsDictionary.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.database.Cursor; +import android.provider.Contacts; +import android.provider.Contacts.People; +import android.util.Log; + +public class ContactsDictionary extends ExpandableDictionary { + + private static final String[] PROJECTION = { + People._ID, + People.NAME, + }; + + private static final int INDEX_NAME = 1; + + private ContentObserver mObserver; + + private boolean mRequiresReload; + + public ContactsDictionary(Context context) { + super(context); + // Perform a managed query. The Activity will handle closing and requerying the cursor + // when needed. + ContentResolver cres = context.getContentResolver(); + + cres.registerContentObserver(People.CONTENT_URI, true, mObserver = new ContentObserver(null) { + @Override + public void onChange(boolean self) { + mRequiresReload = true; + } + }); + + loadDictionary(); + } + + public synchronized void close() { + if (mObserver != null) { + getContext().getContentResolver().unregisterContentObserver(mObserver); + mObserver = null; + } + } + + private synchronized void loadDictionary() { + Cursor cursor = getContext().getContentResolver() + .query(People.CONTENT_URI, PROJECTION, null, null, null); + addWords(cursor); + mRequiresReload = false; + } + + @Override + public synchronized void getWords(final WordComposer codes, final WordCallback callback) { + if (mRequiresReload) loadDictionary(); + super.getWords(codes, callback); + } + + @Override + public synchronized boolean isValidWord(CharSequence word) { + if (mRequiresReload) loadDictionary(); + return super.isValidWord(word); + } + + private void addWords(Cursor cursor) { + clearDictionary(); + + final int maxWordLength = getMaxWordLength(); + if (cursor.moveToFirst()) { + while (!cursor.isAfterLast()) { + String name = cursor.getString(INDEX_NAME); + int len = name.length(); + + // TODO: Better tokenization for non-Latin writing systems + for (int i = 0; i < len; i++) { + if (Character.isLetter(name.charAt(i))) { + int j; + for (j = i + 1; j < len; j++) { + char c = name.charAt(j); + + if (!(c == '-' || c == '\'' || + Character.isLetter(c))) { + break; + } + } + + String word = name.substring(i, j); + i = j - 1; + + // Safeguard against adding really long words. Stack + // may overflow due to recursion + if (word.length() < maxWordLength) { + super.addWord(word, 128); + } + } + } + + cursor.moveToNext(); + } + } + cursor.close(); + } +} diff --git a/src/com/android/inputmethod/latin/LatinIME.java b/src/com/android/inputmethod/latin/LatinIME.java index 5480c6780..222e0c5a2 100644 --- a/src/com/android/inputmethod/latin/LatinIME.java +++ b/src/com/android/inputmethod/latin/LatinIME.java @@ -103,6 +103,7 @@ public class LatinIME extends InputMethodService KeyboardSwitcher mKeyboardSwitcher; private UserDictionary mUserDictionary; + private ContactsDictionary mContactsDictionary; private ExpandableDictionary mAutoDictionary; private String mLocale; @@ -188,8 +189,10 @@ public class LatinIME extends InputMethodService mSuggest = new Suggest(this, R.raw.main); mSuggest.setCorrectionMode(mCorrectionMode); mUserDictionary = new UserDictionary(this); + mContactsDictionary = new ContactsDictionary(this); mAutoDictionary = new AutoDictionary(this); mSuggest.setUserDictionary(mUserDictionary); + mSuggest.setContactsDictionary(mContactsDictionary); mSuggest.setAutoDictionary(mAutoDictionary); mWordSeparators = getResources().getString(R.string.word_separators); mSentenceSeparators = getResources().getString(R.string.sentence_separators); @@ -197,6 +200,7 @@ public class LatinIME extends InputMethodService @Override public void onDestroy() { mUserDictionary.close(); + mContactsDictionary.close(); unregisterReceiver(mReceiver); super.onDestroy(); } diff --git a/src/com/android/inputmethod/latin/Suggest.java b/src/com/android/inputmethod/latin/Suggest.java index 9926c2c1c..077d76aba 100755 --- a/src/com/android/inputmethod/latin/Suggest.java +++ b/src/com/android/inputmethod/latin/Suggest.java @@ -43,6 +43,8 @@ public class Suggest implements Dictionary.WordCallback { private Dictionary mAutoDictionary; + private Dictionary mContactsDictionary; + private int mPrefMaxSuggestions = 12; private int[] mPriorities = new int[mPrefMaxSuggestions]; @@ -81,6 +83,13 @@ public class Suggest implements Dictionary.WordCallback { public void setUserDictionary(Dictionary userDictionary) { mUserDictionary = userDictionary; } + + /** + * Sets an optional contacts dictionary resource to be loaded. + */ + public void setContactsDictionary(Dictionary userDictionary) { + mContactsDictionary = userDictionary; + } public void setAutoDictionary(Dictionary autoDictionary) { mAutoDictionary = autoDictionary; @@ -110,8 +119,8 @@ public class Suggest implements Dictionary.WordCallback { if (len <= 2) return true; int matching = 0; for (int i = 0; i < len; i++) { - if (UserDictionary.toLowerCase(original.charAt(i)) - == UserDictionary.toLowerCase(suggestion.charAt(i))) { + if (ExpandableDictionary.toLowerCase(original.charAt(i)) + == ExpandableDictionary.toLowerCase(suggestion.charAt(i))) { matching++; } } @@ -148,8 +157,14 @@ public class Suggest implements Dictionary.WordCallback { } // Search the dictionary only if there are at least 2 characters if (wordComposer.size() > 1) { - if (mUserDictionary != null) { - mUserDictionary.getWords(wordComposer, this); + if (mUserDictionary != null || mContactsDictionary != null) { + if (mUserDictionary != null) { + mUserDictionary.getWords(wordComposer, this); + } + if (mContactsDictionary != null) { + mContactsDictionary.getWords(wordComposer, this); + } + if (mSuggestions.size() > 0 && isValidWord(mOriginalWord)) { mHaveCorrection = true; } @@ -263,7 +278,8 @@ public class Suggest implements Dictionary.WordCallback { return (mCorrectionMode == CORRECTION_FULL && mMainDict.isValidWord(word)) || (mCorrectionMode > CORRECTION_NONE && ((mUserDictionary != null && mUserDictionary.isValidWord(word))) - || (mAutoDictionary != null && mAutoDictionary.isValidWord(word))); + || (mAutoDictionary != null && mAutoDictionary.isValidWord(word)) + || (mContactsDictionary != null && mContactsDictionary.isValidWord(word))); } private void collectGarbage() {