Merge "Load UserDictionary and AutoDictionary in a background thread."
commit
21daa53245
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
@ -150,27 +123,5 @@ 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -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,26 +47,27 @@ 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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
loadDictionary();
|
loadDictionary();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void close() {
|
public synchronized void close() {
|
||||||
if (mObserver != null) {
|
if (mObserver != null) {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue