Merge "Load UserDictionary and AutoDictionary in a background thread."

main
Amith Yamasani 2010-03-10 12:59:16 -08:00 committed by Android (Google) Code Review
commit 21daa53245
4 changed files with 94 additions and 87 deletions

View File

@ -96,11 +96,14 @@ public class AutoDictionary extends ExpandableDictionary {
return frequency >= VALIDITY_THRESHOLD; return frequency >= VALIDITY_THRESHOLD;
} }
@Override
public void close() { public void close() {
mOpenHelper.close(); mOpenHelper.close();
super.close();
} }
private void loadDictionary() { @Override
public void loadDictionaryAsync() {
// Load the words that correspond to the current input locale // Load the words that correspond to the current input locale
Cursor cursor = query(COLUMN_LOCALE + "=?", new String[] { mLocale }); Cursor cursor = query(COLUMN_LOCALE + "=?", new String[] { mLocale });
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
@ -183,15 +186,6 @@ public class AutoDictionary extends ExpandableDictionary {
return c; return c;
} }
private boolean insert(ContentValues values) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
long rowId = db.insert(AUTODICT_TABLE_NAME, Words.WORD, values);
if (rowId > 0) {
return true;
}
return false;
}
private int delete(String where, String[] whereArgs) { private int delete(String where, String[] whereArgs) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase(); SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count = db.delete(AUTODICT_TABLE_NAME, where, whereArgs); int count = db.delete(AUTODICT_TABLE_NAME, where, whereArgs);

View File

@ -35,15 +35,8 @@ public class ContactsDictionary extends ExpandableDictionary {
private ContentObserver mObserver; private ContentObserver mObserver;
private boolean mRequiresReload;
private long mLastLoadedContacts; private long mLastLoadedContacts;
private boolean mUpdatingContacts;
// Use this lock before touching mUpdatingContacts & mRequiresDownload
private Object mUpdatingLock = new Object();
public ContactsDictionary(Context context) { public ContactsDictionary(Context context) {
super(context); super(context);
// Perform a managed query. The Activity will handle closing and requerying the cursor // Perform a managed query. The Activity will handle closing and requerying the cursor
@ -53,15 +46,10 @@ public class ContactsDictionary extends ExpandableDictionary {
cres.registerContentObserver(Contacts.CONTENT_URI, true, mObserver = new ContentObserver(null) { cres.registerContentObserver(Contacts.CONTENT_URI, true, mObserver = new ContentObserver(null) {
@Override @Override
public void onChange(boolean self) { public void onChange(boolean self) {
synchronized (mUpdatingLock) { setRequiresReload(true);
mRequiresReload = true;
}
} }
}); });
loadDictionary();
synchronized (mUpdatingLock) {
loadDictionaryAsyncLocked();
}
} }
public synchronized void close() { public synchronized void close() {
@ -69,41 +57,26 @@ public class ContactsDictionary extends ExpandableDictionary {
getContext().getContentResolver().unregisterContentObserver(mObserver); getContext().getContentResolver().unregisterContentObserver(mObserver);
mObserver = null; mObserver = null;
} }
super.close();
} }
private synchronized void loadDictionaryAsyncLocked() { @Override
public void startDictionaryLoadingTaskLocked() {
long now = SystemClock.uptimeMillis(); long now = SystemClock.uptimeMillis();
if (mLastLoadedContacts == 0 if (mLastLoadedContacts == 0
|| now - mLastLoadedContacts > 30 * 60 * 1000 /* 30 minutes */) { || now - mLastLoadedContacts > 30 * 60 * 1000 /* 30 minutes */) {
if (!mUpdatingContacts) { super.startDictionaryLoadingTaskLocked();
mUpdatingContacts = true;
mRequiresReload = false;
new LoadContactsTask().execute();
}
} }
} }
@Override @Override
public synchronized void getWords(final WordComposer codes, final WordCallback callback, public void loadDictionaryAsync() {
int[] nextLettersFrequencies) { Cursor cursor = getContext().getContentResolver()
synchronized (mUpdatingLock) { .query(Contacts.CONTENT_URI, PROJECTION, null, null, null);
// If we need to update, start off a background task if (cursor != null) {
if (mRequiresReload) loadDictionaryAsyncLocked(); addWords(cursor);
// Currently updating contacts, don't return any results.
if (mUpdatingContacts) return;
} }
super.getWords(codes, callback, nextLettersFrequencies); mLastLoadedContacts = SystemClock.uptimeMillis();
}
@Override
public synchronized boolean isValidWord(CharSequence word) {
synchronized (mUpdatingLock) {
// If we need to update, start off a background task
if (mRequiresReload) loadDictionaryAsyncLocked();
if (mUpdatingContacts) return false;
}
return super.isValidWord(word);
} }
private void addWords(Cursor cursor) { private void addWords(Cursor cursor) {
@ -151,26 +124,4 @@ public class ContactsDictionary extends ExpandableDictionary {
cursor.close(); cursor.close();
} }
private class LoadContactsTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... v) {
Cursor cursor = getContext().getContentResolver()
.query(Contacts.CONTENT_URI, PROJECTION, null, null, null);
if (cursor != null) {
addWords(cursor);
}
mLastLoadedContacts = SystemClock.uptimeMillis();
return null;
}
@Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
synchronized (mUpdatingLock) {
mUpdatingContacts = false;
}
super.onPostExecute(result);
}
}
} }

View File

@ -16,7 +16,11 @@
package com.android.inputmethod.latin; package com.android.inputmethod.latin;
import com.android.inputmethod.latin.Dictionary.WordCallback;
import android.content.Context; import android.content.Context;
import android.os.AsyncTask;
import android.os.SystemClock;
/** /**
* Base class for an in-memory dictionary that can grow dynamically and can * Base class for an in-memory dictionary that can grow dynamically and can
@ -32,6 +36,13 @@ public class ExpandableDictionary extends Dictionary {
public static final int MAX_WORD_LENGTH = 32; public static final int MAX_WORD_LENGTH = 32;
private static final char QUOTE = '\''; private static final char QUOTE = '\'';
private boolean mRequiresReload;
private boolean mUpdatingDictionary;
// Use this lock before touching mUpdatingDictionary & mRequiresDownload
private Object mUpdatingLock = new Object();
static class Node { static class Node {
char code; char code;
int frequency; int frequency;
@ -70,6 +81,34 @@ public class ExpandableDictionary extends Dictionary {
mCodes = new int[MAX_WORD_LENGTH][]; mCodes = new int[MAX_WORD_LENGTH][];
} }
public void loadDictionary() {
synchronized (mUpdatingLock) {
startDictionaryLoadingTaskLocked();
}
}
public void startDictionaryLoadingTaskLocked() {
if (!mUpdatingDictionary) {
mUpdatingDictionary = true;
mRequiresReload = false;
new LoadDictionaryTask().execute();
}
}
public void setRequiresReload(boolean reload) {
synchronized (mUpdatingLock) {
mRequiresReload = reload;
}
}
public boolean getRequiresReload() {
return mRequiresReload;
}
/** Override to load your dictionary here, on a background thread. */
public void loadDictionaryAsync() {
}
Context getContext() { Context getContext() {
return mContext; return mContext;
} }
@ -119,6 +158,13 @@ public class ExpandableDictionary extends Dictionary {
@Override @Override
public void getWords(final WordComposer codes, final WordCallback callback, public void getWords(final WordComposer codes, final WordCallback callback,
int[] nextLettersFrequencies) { int[] nextLettersFrequencies) {
synchronized (mUpdatingLock) {
// If we need to update, start off a background task
if (mRequiresReload) startDictionaryLoadingTaskLocked();
// Currently updating contacts, don't return any results.
if (mUpdatingDictionary) return;
}
mInputLength = codes.size(); mInputLength = codes.size();
mNextLettersFrequencies = nextLettersFrequencies; mNextLettersFrequencies = nextLettersFrequencies;
if (mCodes.length < mInputLength) mCodes = new int[mInputLength][]; if (mCodes.length < mInputLength) mCodes = new int[mInputLength][];
@ -135,6 +181,11 @@ public class ExpandableDictionary extends Dictionary {
@Override @Override
public synchronized boolean isValidWord(CharSequence word) { public synchronized boolean isValidWord(CharSequence word) {
synchronized (mUpdatingLock) {
// If we need to update, start off a background task
if (mRequiresReload) startDictionaryLoadingTaskLocked();
if (mUpdatingDictionary) return false;
}
final int freq = getWordFrequencyRec(mRoots, word, 0, word.length()); final int freq = getWordFrequencyRec(mRoots, word, 0, word.length());
return freq > -1; return freq > -1;
} }
@ -277,6 +328,24 @@ public class ExpandableDictionary extends Dictionary {
mRoots = new NodeArray(); mRoots = new NodeArray();
} }
private class LoadDictionaryTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... v) {
loadDictionaryAsync();
return null;
}
@Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
synchronized (mUpdatingLock) {
mUpdatingDictionary = false;
}
super.onPostExecute(result);
}
}
static char toLowerCase(char c) { static char toLowerCase(char c) {
if (c < BASE_CHARS.length) { if (c < BASE_CHARS.length) {
c = BASE_CHARS[c]; c = BASE_CHARS[c];

View File

@ -16,16 +16,11 @@
package com.android.inputmethod.latin; package com.android.inputmethod.latin;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.database.ContentObserver; import android.database.ContentObserver;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri;
import android.provider.UserDictionary.Words; import android.provider.UserDictionary.Words;
public class UserDictionary extends ExpandableDictionary { public class UserDictionary extends ExpandableDictionary {
@ -40,8 +35,6 @@ public class UserDictionary extends ExpandableDictionary {
private static final int INDEX_FREQUENCY = 2; private static final int INDEX_FREQUENCY = 2;
private ContentObserver mObserver; private ContentObserver mObserver;
private boolean mRequiresReload;
private String mLocale; private String mLocale;
public UserDictionary(Context context, String locale) { public UserDictionary(Context context, String locale) {
@ -54,7 +47,7 @@ public class UserDictionary extends ExpandableDictionary {
cres.registerContentObserver(Words.CONTENT_URI, true, mObserver = new ContentObserver(null) { cres.registerContentObserver(Words.CONTENT_URI, true, mObserver = new ContentObserver(null) {
@Override @Override
public void onChange(boolean self) { public void onChange(boolean self) {
mRequiresReload = true; setRequiresReload(true);
} }
}); });
@ -66,14 +59,15 @@ public class UserDictionary extends ExpandableDictionary {
getContext().getContentResolver().unregisterContentObserver(mObserver); getContext().getContentResolver().unregisterContentObserver(mObserver);
mObserver = null; mObserver = null;
} }
super.close();
} }
private synchronized void loadDictionary() { @Override
public void loadDictionaryAsync() {
Cursor cursor = getContext().getContentResolver() Cursor cursor = getContext().getContentResolver()
.query(Words.CONTENT_URI, PROJECTION, "(locale IS NULL) or (locale=?)", .query(Words.CONTENT_URI, PROJECTION, "(locale IS NULL) or (locale=?)",
new String[] { mLocale }, null); new String[] { mLocale }, null);
addWords(cursor); addWords(cursor);
mRequiresReload = false;
} }
/** /**
@ -86,7 +80,8 @@ public class UserDictionary extends ExpandableDictionary {
*/ */
@Override @Override
public synchronized void addWord(String word, int frequency) { public synchronized void addWord(String word, int frequency) {
if (mRequiresReload) loadDictionary(); // Force load the dictionary here synchronously
if (getRequiresReload()) loadDictionaryAsync();
// Safeguard against adding long words. Can cause stack overflow. // Safeguard against adding long words. Can cause stack overflow.
if (word.length() >= getMaxWordLength()) return; if (word.length() >= getMaxWordLength()) return;
@ -101,19 +96,17 @@ public class UserDictionary extends ExpandableDictionary {
getContext().getContentResolver().insert(Words.CONTENT_URI, values); getContext().getContentResolver().insert(Words.CONTENT_URI, values);
// In case the above does a synchronous callback of the change observer // In case the above does a synchronous callback of the change observer
mRequiresReload = false; setRequiresReload(false);
} }
@Override @Override
public synchronized void getWords(final WordComposer codes, final WordCallback callback, public synchronized void getWords(final WordComposer codes, final WordCallback callback,
int[] nextLettersFrequencies) { int[] nextLettersFrequencies) {
if (mRequiresReload) loadDictionary();
super.getWords(codes, callback, nextLettersFrequencies); super.getWords(codes, callback, nextLettersFrequencies);
} }
@Override @Override
public synchronized boolean isValidWord(CharSequence word) { public synchronized boolean isValidWord(CharSequence word) {
if (mRequiresReload) loadDictionary();
return super.isValidWord(word); return super.isValidWord(word);
} }