Merge "Add a settings activity for the spell checker."
diff --git a/java/res/values-sw600dp/config.xml b/java/res/values-sw600dp/config.xml
index ade376a..a591677 100644
--- a/java/res/values-sw600dp/config.xml
+++ b/java/res/values-sw600dp/config.xml
@@ -20,7 +20,7 @@
 
 <resources>
     <bool name="config_enable_show_settings_key_option">true</bool>
-    <bool name="config_default_show_settings_key">true</bool>
+    <bool name="config_default_show_settings_key">false</bool>
     <bool name="config_enable_show_voice_key_option">false</bool>
     <bool name="config_enable_show_popup_on_keypress_option">false</bool>
     <bool name="config_enable_show_recorrection_option">false</bool>
diff --git a/java/res/xml-sw600dp/kbd_rows_russian.xml b/java/res/xml-sw600dp/kbd_rows_russian.xml
index 7588f6c..2f4b95e 100644
--- a/java/res/xml-sw600dp/kbd_rows_russian.xml
+++ b/java/res/xml-sw600dp/kbd_rows_russian.xml
@@ -105,8 +105,7 @@
         <Key
             latin:keyLabel="т" />
         <Key
-            latin:keyLabel="ь"
-            latin:popupCharacters="@string/alternates_for_cyrillic_soft_sign" />
+            latin:keyLabel="ь" />
         <Key
             latin:keyLabel="б" />
         <Key
diff --git a/java/res/xml/kbd_rows_russian.xml b/java/res/xml/kbd_rows_russian.xml
index 216d749..3aeb52b 100644
--- a/java/res/xml/kbd_rows_russian.xml
+++ b/java/res/xml/kbd_rows_russian.xml
@@ -69,6 +69,7 @@
             latin:popupCharacters="0" />
         <Key
             latin:keyLabel="х"
+            latin:popupCharacters="@string/alternates_for_cyrillic_soft_sign"
             latin:keyWidth="fillRight" />
     </Row>
     <Row
diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml
index 2ff82f9..84ceae6 100644
--- a/java/res/xml/prefs.xml
+++ b/java/res/xml/prefs.xml
@@ -53,7 +53,7 @@
             android:key="show_settings_key"
             android:title="@string/prefs_settings_key"
             android:persistent="true"
-            android:defaultValue="false" />
+            android:defaultValue="@bool/config_default_show_settings_key" />
         <ListPreference
             android:key="voice_mode"
             android:title="@string/voice_input"
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index 170edad..360cf21 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -17,6 +17,8 @@
 package com.android.inputmethod.latin;
 
 import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.AssetFileDescriptor;
 import android.content.res.Resources;
 import android.util.Log;
@@ -39,10 +41,27 @@
      */
     private static final String TAG = BinaryDictionaryGetter.class.getSimpleName();
 
+    /**
+     * Name of the common preferences name to know which word list are on and which are off.
+     */
+    private static final String COMMON_PREFERENCES_NAME = "LatinImeDictPrefs";
+
     // Prevents this from being instantiated
     private BinaryDictionaryGetter() {}
 
     /**
+     * Returns whether we may want to use this character as part of a file name.
+     *
+     * This basically only accepts ascii letters and numbers, and rejects everything else.
+     */
+    private static boolean isFileNameCharacter(int codePoint) {
+        if (codePoint >= 0x30 && codePoint <= 0x39) return true; // Digit
+        if (codePoint >= 0x41 && codePoint <= 0x5A) return true; // Uppercase
+        if (codePoint >= 0x61 && codePoint <= 0x7A) return true; // Lowercase
+        return codePoint == '_'; // Underscore
+    }
+
+    /**
      * Escapes a string for any characters that may be suspicious for a file or directory name.
      *
      * Concretely this does a sort of URL-encoding except it will encode everything that's not
@@ -50,17 +69,35 @@
      * we cannot allow here)
      */
     // TODO: create a unit test for this method
-    private static String replaceFileNameDangerousCharacters(String name) {
+    private static String replaceFileNameDangerousCharacters(final String name) {
         // This assumes '%' is fully available as a non-separator, normal
         // character in a file name. This is probably true for all file systems.
         final StringBuilder sb = new StringBuilder();
         for (int i = 0; i < name.length(); ++i) {
             final int codePoint = name.codePointAt(i);
-            if (Character.isLetterOrDigit(codePoint) || '_' == codePoint) {
+            if (isFileNameCharacter(codePoint)) {
                 sb.appendCodePoint(codePoint);
             } else {
-                sb.append('%');
-                sb.append(Integer.toHexString(codePoint));
+                // 6 digits - unicode is limited to 21 bits
+                sb.append(String.format((Locale)null, "%%%1$06x", codePoint));
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Reverse escaping done by replaceFileNameDangerousCharacters.
+     */
+    private static String getWordListIdFromFileName(final String fname) {
+        final StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < fname.length(); ++i) {
+            final int codePoint = fname.codePointAt(i);
+            if ('%' != codePoint) {
+                sb.appendCodePoint(codePoint);
+            } else {
+                final int encodedCodePoint = Integer.parseInt(fname.substring(i + 1, i + 7), 16);
+                i += 6;
+                sb.appendCodePoint(encodedCodePoint);
             }
         }
         return sb.toString();
@@ -132,10 +169,28 @@
             final Context context) {
         final String directoryName = getCacheDirectoryForLocale(locale, context);
         final File[] cacheFiles = new File(directoryName).listFiles();
+        // TODO: Never return null. Fallback on the built-in dictionary, and if that's
+        // not present or disabled, then return an empty list.
         if (null == cacheFiles) return null;
 
+        final SharedPreferences dictPackSettings;
+        try {
+            final String dictPackName = context.getString(R.string.dictionary_pack_package_name);
+            final Context dictPackContext = context.createPackageContext(dictPackName, 0);
+            dictPackSettings = dictPackContext.getSharedPreferences(COMMON_PREFERENCES_NAME,
+                    Context.MODE_WORLD_READABLE | Context.MODE_MULTI_PROCESS);
+        } catch (NameNotFoundException e) {
+            // The dictionary pack is not installed...
+            // TODO: fallback on the built-in dict, see the TODO above
+            Log.e(TAG, "Could not find a dictionary pack");
+            return null;
+        }
+
         final ArrayList<AssetFileAddress> fileList = new ArrayList<AssetFileAddress>();
         for (File f : cacheFiles) {
+            final String wordListId = getWordListIdFromFileName(f.getName());
+            final boolean isActive = dictPackSettings.getBoolean(wordListId, true);
+            if (!isActive) continue;
             if (f.canRead()) {
                 fileList.add(AssetFileAddress.makeFromFileName(f.getPath()));
             } else {