From db5aedb5a5eea5224e5a732b689c97eead2e35f4 Mon Sep 17 00:00:00 2001
From: Jean Chalard <jchalard@google.com>
Date: Thu, 10 Nov 2011 08:21:42 +0900
Subject: [PATCH] Add an option to use contacts or not while spell checking

Bug: 5447495
Change-Id: Iffb09895676c3dd1a79d1699b0eac865282508d4
---
 java/res/values/strings.xml                   |  8 +-
 java/res/xml/spell_checker_settings.xml       |  6 +-
 java/res/xml/spellchecker.xml                 |  3 +-
 .../latin/DictionaryCollection.java           | 22 ++++-
 .../AndroidSpellCheckerService.java           | 96 ++++++++++++++++---
 5 files changed, 112 insertions(+), 23 deletions(-)

diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index e00547a626..8f999215e8 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -31,11 +31,11 @@
     <!-- Title for the spell checking service settings screen -->
     <string name="android_spell_checker_settings">Spell checking settings</string>
 
-    <!-- Title for the "use proximity" option for spell checking [CHAR LIMIT=25] -->
-    <string name="use_proximity_option_title">Use proximity data</string>
+    <!-- Title for the spell checker option to turn on/off contact names lookup [CHAR LIMIT=25] -->
+    <string name="use_contacts_for_spellchecking_option_title">Look up contact names</string>
 
-    <!-- Description for the "use proximity" option for spell checking [CHAR LIMIT=65] -->
-    <string name="use_proximity_option_summary">Use a keyboard-like proximity algorithm for spell checking</string>
+    <!-- Description for the spell checker option to turn on/off contact names lookup. [CHAR LIMIT=65] -->
+    <string name="use_contacts_for_spellchecking_option_summary">Spell checker uses entries from your contact list</string>
 
     <!-- Option to provide vibrate/haptic feedback on keypress -->
     <string name="vibrate_on_keypress">Vibrate on keypress</string>
diff --git a/java/res/xml/spell_checker_settings.xml b/java/res/xml/spell_checker_settings.xml
index f402555c95..222b98b6bd 100644
--- a/java/res/xml/spell_checker_settings.xml
+++ b/java/res/xml/spell_checker_settings.xml
@@ -18,9 +18,9 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:title="@string/android_spell_checker_settings">
   <CheckBoxPreference
-     android:key="use_proximity"
-     android:title="@string/use_proximity_option_title"
-     android:summary="@string/use_proximity_option_summary"
+     android:key="pref_spellcheck_use_contacts"
+     android:title="@string/use_contacts_for_spellchecking_option_title"
+     android:summary="@string/use_contacts_for_spellchecking_option_summary"
      android:persistent="true"
      android:defaultValue="true" />
 </PreferenceScreen>
diff --git a/java/res/xml/spellchecker.xml b/java/res/xml/spellchecker.xml
index 30fac5b205..d1269d878f 100644
--- a/java/res/xml/spellchecker.xml
+++ b/java/res/xml/spellchecker.xml
@@ -21,7 +21,8 @@
 <!-- for the spell checker -->
 
 <spell-checker xmlns:android="http://schemas.android.com/apk/res/android"
-        android:label="@string/spell_checker_service_name">
+        android:label="@string/spell_checker_service_name"
+        android:settingsActivity="com.android.inputmethod.latin.spellcheck.SpellCheckerSettingsActivity">
     <subtype
             android:label="@string/subtype_generic"
             android:subtypeLocale="en"
diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
index 739153044b..c19a5a718a 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
@@ -18,6 +18,8 @@ package com.android.inputmethod.latin;
 
 import com.android.inputmethod.keyboard.ProximityInfo;
 
+import android.util.Log;
+
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -27,7 +29,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
  * Class for a collection of dictionaries that behave like one dictionary.
  */
 public class DictionaryCollection extends Dictionary {
-
+    private final String TAG = DictionaryCollection.class.getSimpleName();
     protected final List<Dictionary> mDictionaries;
 
     public DictionaryCollection() {
@@ -75,7 +77,21 @@ public class DictionaryCollection extends Dictionary {
             dict.close();
     }
 
-    public void addDictionary(Dictionary newDict) {
-        if (null != newDict) mDictionaries.add(newDict);
+    // Warning: this is not thread-safe. Take necessary precaution when calling.
+    public void addDictionary(final Dictionary newDict) {
+        if (null == newDict) return;
+        if (mDictionaries.contains(newDict)) {
+            Log.w(TAG, "This collection already contains this dictionary: " + newDict);
+        }
+        mDictionaries.add(newDict);
+    }
+
+    // Warning: this is not thread-safe. Take necessary precaution when calling.
+    public void removeDictionary(final Dictionary dict) {
+        if (mDictionaries.contains(dict)) {
+            mDictionaries.remove(dict);
+        } else {
+            Log.w(TAG, "This collection does not contain this dictionary: " + dict);
+        }
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index 93540ab126..9dfbe7a54e 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -17,7 +17,9 @@
 package com.android.inputmethod.latin.spellcheck;
 
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.content.res.Resources;
+import android.preference.PreferenceManager;
 import android.service.textservice.SpellCheckerService;
 import android.text.TextUtils;
 import android.util.Log;
@@ -41,21 +43,27 @@ import com.android.inputmethod.latin.Utils;
 import com.android.inputmethod.latin.WhitelistDictionary;
 import com.android.inputmethod.latin.WordComposer;
 
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.Locale;
 import java.util.Map;
 import java.util.TreeMap;
+import java.util.HashSet;
 
 /**
  * Service for spell checking, using LatinIME's dictionaries and mechanisms.
  */
-public class AndroidSpellCheckerService extends SpellCheckerService {
+public class AndroidSpellCheckerService extends SpellCheckerService
+        implements SharedPreferences.OnSharedPreferenceChangeListener {
     private static final String TAG = AndroidSpellCheckerService.class.getSimpleName();
     private static final boolean DBG = false;
     private static final int POOL_SIZE = 2;
 
+    public static final String PREF_USE_CONTACTS_KEY = "pref_spellcheck_use_contacts";
+
     private static final int CAPITALIZE_NONE = 0; // No caps, or mixed case
     private static final int CAPITALIZE_FIRST = 1; // First only
     private static final int CAPITALIZE_ALL = 2; // All caps
@@ -84,6 +92,12 @@ public class AndroidSpellCheckerService extends SpellCheckerService {
     private double mSuggestionThreshold;
     // The threshold for a suggestion to be considered "recommended".
     private double mRecommendedThreshold;
+    // Whether to use the contacts dictionary
+    private boolean mUseContactsDictionary;
+    private final Object mUseContactsLock = new Object();
+
+    private final HashSet<WeakReference<DictionaryCollection>> mDictionaryCollectionsList =
+            new HashSet<WeakReference<DictionaryCollection>>();
 
     @Override public void onCreate() {
         super.onCreate();
@@ -91,6 +105,57 @@ public class AndroidSpellCheckerService extends SpellCheckerService {
                 Double.parseDouble(getString(R.string.spellchecker_suggestion_threshold_value));
         mRecommendedThreshold =
                 Double.parseDouble(getString(R.string.spellchecker_recommended_threshold_value));
+        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+        prefs.registerOnSharedPreferenceChangeListener(this);
+        onSharedPreferenceChanged(prefs, PREF_USE_CONTACTS_KEY);
+    }
+
+    @Override
+    public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
+        if (!PREF_USE_CONTACTS_KEY.equals(key)) return;
+        synchronized(mUseContactsLock) {
+            mUseContactsDictionary = prefs.getBoolean(PREF_USE_CONTACTS_KEY, true);
+            if (mUseContactsDictionary) {
+                startUsingContactsDictionaryLocked();
+            } else {
+                stopUsingContactsDictionaryLocked();
+            }
+        }
+    }
+
+    private void startUsingContactsDictionaryLocked() {
+        if (null == mContactsDictionary) {
+            mContactsDictionary = new SynchronouslyLoadedContactsDictionary(this);
+        }
+        final Iterator<WeakReference<DictionaryCollection>> iterator =
+                mDictionaryCollectionsList.iterator();
+        while (iterator.hasNext()) {
+            final WeakReference<DictionaryCollection> dictRef = iterator.next();
+            final DictionaryCollection dict = dictRef.get();
+            if (null == dict) {
+                iterator.remove();
+            } else {
+                dict.addDictionary(mContactsDictionary);
+            }
+        }
+    }
+
+    private void stopUsingContactsDictionaryLocked() {
+        if (null == mContactsDictionary) return;
+        final SynchronouslyLoadedContactsDictionary contactsDict = mContactsDictionary;
+        mContactsDictionary = null;
+        final Iterator<WeakReference<DictionaryCollection>> iterator =
+                mDictionaryCollectionsList.iterator();
+        while (iterator.hasNext()) {
+            final WeakReference<DictionaryCollection> dictRef = iterator.next();
+            final DictionaryCollection dict = dictRef.get();
+            if (null == dict) {
+                iterator.remove();
+            } else {
+                dict.removeDictionary(contactsDict);
+            }
+        }
+        contactsDict.close();
     }
 
     @Override
@@ -274,13 +339,15 @@ public class AndroidSpellCheckerService extends SpellCheckerService {
         for (Dictionary dict : oldWhitelistDictionaries.values()) {
             dict.close();
         }
-        if (null != mContactsDictionary) {
-            // The synchronously loaded contacts dictionary should have been in one
-            // or several pools, but it is shielded against multiple closing and it's
-            // safe to call it several times.
-            final SynchronouslyLoadedContactsDictionary dictToClose = mContactsDictionary;
-            mContactsDictionary = null;
-            dictToClose.close();
+        synchronized(mUseContactsLock) {
+            if (null != mContactsDictionary) {
+                // The synchronously loaded contacts dictionary should have been in one
+                // or several pools, but it is shielded against multiple closing and it's
+                // safe to call it several times.
+                final SynchronouslyLoadedContactsDictionary dictToClose = mContactsDictionary;
+                mContactsDictionary = null;
+                dictToClose.close();
+            }
         }
         return false;
     }
@@ -315,11 +382,16 @@ public class AndroidSpellCheckerService extends SpellCheckerService {
             mWhitelistDictionaries.put(localeStr, whitelistDictionary);
         }
         dictionaryCollection.addDictionary(whitelistDictionary);
-        if (null == mContactsDictionary) {
-            mContactsDictionary = new SynchronouslyLoadedContactsDictionary(this);
+        synchronized(mUseContactsLock) {
+            if (mUseContactsDictionary) {
+                if (null == mContactsDictionary) {
+                    mContactsDictionary = new SynchronouslyLoadedContactsDictionary(this);
+                }
+            }
+            dictionaryCollection.addDictionary(mContactsDictionary);
+            mDictionaryCollectionsList.add(
+                    new WeakReference<DictionaryCollection>(dictionaryCollection));
         }
-        // TODO: add a setting to use or not contacts when checking spelling
-        dictionaryCollection.addDictionary(mContactsDictionary);
         return new DictAndProximity(dictionaryCollection, proximityInfo);
     }
 
-- 
GitLab