Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 283a77f6 authored by Amith Yamasani's avatar Amith Yamasani
Browse files

Load UserDictionary and AutoDictionary in a background thread.

This is to avoid ANRs during bootup, as some of the providers
may not have been initialized yet.

Refactored the ContactsDictionary and moved the async loading
code to ExpandableDictionary to share with the other dicts.

Bug: 2501133
Change-Id: I20393edb6fdf5df2f54ebac8dd04419a592177a2
parent 97e2d110
Loading
Loading
Loading
Loading
+4 −10
Original line number Diff line number Diff line
@@ -96,11 +96,14 @@ public class AutoDictionary extends ExpandableDictionary {
        return frequency >= VALIDITY_THRESHOLD;
    }

    @Override
    public void close() {
        mOpenHelper.close();
        super.close();
    }

    private void loadDictionary() {
    @Override
    public void loadDictionaryAsync() {
        // Load the words that correspond to the current input locale
        Cursor cursor = query(COLUMN_LOCALE + "=?", new String[] { mLocale });
        if (cursor.moveToFirst()) {
@@ -183,15 +186,6 @@ public class AutoDictionary extends ExpandableDictionary {
        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) {
        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
        int count = db.delete(AUTODICT_TABLE_NAME, where, whereArgs);
+12 −61
Original line number Diff line number Diff line
@@ -35,15 +35,8 @@ public class ContactsDictionary extends ExpandableDictionary {

    private ContentObserver mObserver;

    private boolean mRequiresReload;

    private long mLastLoadedContacts;

    private boolean mUpdatingContacts;

    // Use this lock before touching mUpdatingContacts & mRequiresDownload
    private Object mUpdatingLock = new Object();

    public ContactsDictionary(Context context) {
        super(context);
        // 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) {
            @Override
            public void onChange(boolean self) {
                synchronized (mUpdatingLock) {
                    mRequiresReload = true;
                }
                setRequiresReload(true);
            }
        });

        synchronized (mUpdatingLock) {
            loadDictionaryAsyncLocked();
        }
        loadDictionary();
    }

    public synchronized void close() {
@@ -69,41 +57,26 @@ public class ContactsDictionary extends ExpandableDictionary {
            getContext().getContentResolver().unregisterContentObserver(mObserver);
            mObserver = null;
        }
        super.close();
    }

    private synchronized void loadDictionaryAsyncLocked() {
    @Override
    public void startDictionaryLoadingTaskLocked() {
        long now = SystemClock.uptimeMillis();
        if (mLastLoadedContacts == 0
                || now - mLastLoadedContacts > 30 * 60 * 1000 /* 30 minutes */) {
            if (!mUpdatingContacts) {
                mUpdatingContacts = true;
                mRequiresReload = false;
                new LoadContactsTask().execute();
            }
            super.startDictionaryLoadingTaskLocked();
        }
    }

    @Override
    public synchronized void getWords(final WordComposer codes, final WordCallback callback,
            int[] nextLettersFrequencies) {
        synchronized (mUpdatingLock) {
            // If we need to update, start off a background task
            if (mRequiresReload) loadDictionaryAsyncLocked();
            // Currently updating contacts, don't return any results.
            if (mUpdatingContacts) return;
        }
        super.getWords(codes, callback, nextLettersFrequencies);
    }

    @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;
    public void loadDictionaryAsync() {
        Cursor cursor = getContext().getContentResolver()
                .query(Contacts.CONTENT_URI, PROJECTION, null, null, null);
        if (cursor != null) {
            addWords(cursor);
        }

        return super.isValidWord(word);
        mLastLoadedContacts = SystemClock.uptimeMillis();
    }

    private void addWords(Cursor cursor) {
@@ -151,26 +124,4 @@ public class ContactsDictionary extends ExpandableDictionary {
        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);
        }
        
    }
}
+69 −0
Original line number Diff line number Diff line
@@ -16,7 +16,11 @@

package com.android.inputmethod.latin;

import com.android.inputmethod.latin.Dictionary.WordCallback;

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
@@ -32,6 +36,13 @@ public class ExpandableDictionary extends Dictionary {
    public static final int MAX_WORD_LENGTH = 32;
    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 {
        char code;
        int frequency;
@@ -70,6 +81,34 @@ public class ExpandableDictionary extends Dictionary {
        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() {
        return mContext;
    }
@@ -119,6 +158,13 @@ public class ExpandableDictionary extends Dictionary {
    @Override
    public void getWords(final WordComposer codes, final WordCallback callback,
            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();
        mNextLettersFrequencies = nextLettersFrequencies;
        if (mCodes.length < mInputLength) mCodes = new int[mInputLength][];
@@ -135,6 +181,11 @@ public class ExpandableDictionary extends Dictionary {

    @Override
    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());
        return freq > -1;
    }
@@ -277,6 +328,24 @@ public class ExpandableDictionary extends Dictionary {
        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) {
        if (c < BASE_CHARS.length) {
            c = BASE_CHARS[c];
+9 −16
Original line number Diff line number Diff line
@@ -16,16 +16,11 @@

package com.android.inputmethod.latin;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.provider.UserDictionary.Words;

public class UserDictionary extends ExpandableDictionary {
@@ -40,8 +35,6 @@ public class UserDictionary extends ExpandableDictionary {
    private static final int INDEX_FREQUENCY = 2;
    
    private ContentObserver mObserver;
    
    private boolean mRequiresReload;
    private String mLocale;

    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) {
            @Override
            public void onChange(boolean self) {
                mRequiresReload = true;
                setRequiresReload(true);
            }
        });

@@ -66,14 +59,15 @@ public class UserDictionary extends ExpandableDictionary {
            getContext().getContentResolver().unregisterContentObserver(mObserver);
            mObserver = null;
        }
        super.close();
    }

    private synchronized void loadDictionary() {
    @Override
    public void loadDictionaryAsync() {
        Cursor cursor = getContext().getContentResolver()
                .query(Words.CONTENT_URI, PROJECTION, "(locale IS NULL) or (locale=?)", 
                        new String[] { mLocale }, null);
        addWords(cursor);
        mRequiresReload = false;
    }

    /**
@@ -86,7 +80,8 @@ public class UserDictionary extends ExpandableDictionary {
     */
    @Override
    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.
        if (word.length() >= getMaxWordLength()) return;

@@ -101,19 +96,17 @@ public class UserDictionary extends ExpandableDictionary {

        getContext().getContentResolver().insert(Words.CONTENT_URI, values);
        // In case the above does a synchronous callback of the change observer
        mRequiresReload = false;
        setRequiresReload(false);
    }

    @Override
    public synchronized void getWords(final WordComposer codes, final WordCallback callback,
            int[] nextLettersFrequencies) {
        if (mRequiresReload) loadDictionary();
        super.getWords(codes, callback, nextLettersFrequencies);
    }

    @Override
    public synchronized boolean isValidWord(CharSequence word) {
        if (mRequiresReload) loadDictionary();
        return super.isValidWord(word);
    }