Support ICS on the user dictionary settings

Bug: 8600958
Change-Id: I50e9228afc7b04147eb7c4734d3145abe8859a30
diff --git a/java/res/layout/user_dictionary_item.xml b/java/res/layout/user_dictionary_item.xml
index 3062ed8..56bad77 100644
--- a/java/res/layout/user_dictionary_item.xml
+++ b/java/res/layout/user_dictionary_item.xml
@@ -13,40 +13,39 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:background="?android:attr/selectableItemBackground"
     android:gravity="center_vertical"
-    android:paddingEnd="?android:attr/scrollbarSize"
-    android:background="?android:attr/selectableItemBackground" >
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:paddingEnd="?android:attr/scrollbarSize" >
 
-  <RelativeLayout android:layout_width="wrap_content"
-                  android:layout_height="wrap_content"
-                  android:layout_marginStart="15dip"
-                  android:layout_marginEnd="6dip"
-                  android:layout_marginTop="6dip"
-                  android:layout_marginBottom="6dip"
-                  android:layout_weight="1">
+    <RelativeLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:padding="6dip"
+        android:layout_weight="1" >
 
-    <TextView android:id="@+android:id/text1"
-              android:layout_width="wrap_content"
-              android:layout_height="wrap_content"
-              android:singleLine="true"
-              android:textAppearance="?android:attr/textAppearanceMedium"
-              android:ellipsize="marquee"
-              android:fadingEdge="horizontal" />
+        <TextView
+            android:id="@+android:id/text1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceMedium" />
 
-    <TextView android:id="@+android:id/text2"
-              android:layout_width="wrap_content"
-              android:layout_height="wrap_content"
-              android:layout_below="@android:id/text1"
-              android:layout_alignStart="@android:id/text1"
-              android:textAppearance="?android:attr/textAppearanceSmall"
-              android:textColor="?android:attr/textColorSecondary"
-              android:maxLines="1" />
-
-  </RelativeLayout>
+        <TextView
+            android:id="@+android:id/text2"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignStart="@android:id/text1"
+            android:layout_below="@android:id/text1"
+            android:maxLines="1"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="?android:attr/textColorSecondary"
+            android:visibility="gone" />
+    </RelativeLayout>
 
 </LinearLayout>
diff --git a/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java b/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java
index ff6561c..a0d7641 100644
--- a/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java
@@ -28,6 +28,7 @@
     private static final Method METHOD_addWord = CompatUtils.getMethod(Words.class, "addWord",
             Context.class, String.class, Integer.TYPE, String.class, Locale.class);
 
+    @SuppressWarnings("deprecation")
     public static void addWord(final Context context, final String word, final int freq,
             final String shortcut, final Locale locale) {
         if (hasNewerAddWord()) {
@@ -39,13 +40,18 @@
             if (null == locale) {
                 localeType = Words.LOCALE_TYPE_ALL;
             } else {
-                localeType = Words.LOCALE_TYPE_CURRENT;
+                final Locale currentLocale = context.getResources().getConfiguration().locale;
+                if (locale.equals(currentLocale)) {
+                    localeType = Words.LOCALE_TYPE_CURRENT;
+                } else {
+                    localeType = Words.LOCALE_TYPE_ALL;
+                }
             }
             Words.addWord(context, word, freq, localeType);
         }
     }
 
-    private static final boolean hasNewerAddWord() {
+    public static final boolean hasNewerAddWord() {
         return null != METHOD_addWord;
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/SettingsFragment.java b/java/src/com/android/inputmethod/latin/SettingsFragment.java
index c78064b..830cae9 100644
--- a/java/src/com/android/inputmethod/latin/SettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/SettingsFragment.java
@@ -25,7 +25,6 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.media.AudioManager;
-import android.os.Build;
 import android.os.Bundle;
 import android.preference.CheckBoxPreference;
 import android.preference.ListPreference;
@@ -46,7 +45,7 @@
 
 public final class SettingsFragment extends InputMethodSettingsFragment
         implements SharedPreferences.OnSharedPreferenceChangeListener {
-    private static final boolean DBG_USE_INTERNAL_USER_SETTINGS = false;
+    private static final boolean DBG_USE_INTERNAL_USER_DICTIONARY_SETTINGS = false;
 
     private ListPreference mVoicePreference;
     private ListPreference mShowCorrectionSuggestionsPreference;
@@ -202,13 +201,8 @@
         final Intent editPersonalDictionaryIntent = editPersonalDictionary.getIntent();
         final ResolveInfo ri = context.getPackageManager().resolveActivity(
                 editPersonalDictionaryIntent, PackageManager.MATCH_DEFAULT_ONLY);
-        if (DBG_USE_INTERNAL_USER_SETTINGS || ri == null) {
-            // TODO: Support ICS
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
-                updateUserDictionaryPreference(editPersonalDictionary);
-            } else {
-                removePreference(Settings.PREF_EDIT_PERSONAL_DICTIONARY, getPreferenceScreen());
-            }
+        if (DBG_USE_INTERNAL_USER_DICTIONARY_SETTINGS || ri == null) {
+            updateUserDictionaryPreference(editPersonalDictionary);
         }
 
         if (!Settings.readFromBuildConfigIfGestureInputEnabled(res)) {
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java
index f0dc526..2b6fda3 100644
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.latin.userdictionary;
 
+import com.android.inputmethod.compat.UserDictionaryCompatUtils;
 import com.android.inputmethod.latin.LocaleUtils;
 import com.android.inputmethod.latin.R;
 
@@ -68,18 +69,28 @@
     /* package */ UserDictionaryAddWordContents(final View view, final Bundle args) {
         mWordEditText = (EditText)view.findViewById(R.id.user_dictionary_add_word_text);
         mShortcutEditText = (EditText)view.findViewById(R.id.user_dictionary_add_shortcut);
+        if (!UserDictionarySettings.IS_SHORTCUT_API_SUPPORTED) {
+            mShortcutEditText.setVisibility(View.GONE);
+            view.findViewById(R.id.user_dictionary_add_shortcut_label).setVisibility(View.GONE);
+        }
         final String word = args.getString(EXTRA_WORD);
         if (null != word) {
             mWordEditText.setText(word);
             mWordEditText.setSelection(word.length());
         }
-        final String shortcut = args.getString(EXTRA_SHORTCUT);
-        if (null != shortcut && null != mShortcutEditText) {
-            mShortcutEditText.setText(shortcut);
+        final String shortcut;
+        if (UserDictionarySettings.IS_SHORTCUT_API_SUPPORTED) {
+            shortcut = args.getString(EXTRA_SHORTCUT);
+            if (null != shortcut && null != mShortcutEditText) {
+                mShortcutEditText.setText(shortcut);
+            }
+            mOldShortcut = args.getString(EXTRA_SHORTCUT);
+        } else {
+            shortcut = null;
+            mOldShortcut = null;
         }
         mMode = args.getInt(EXTRA_MODE); // default return value for #getInt() is 0 = MODE_EDIT
         mOldWord = args.getString(EXTRA_WORD);
-        mOldShortcut = args.getString(EXTRA_SHORTCUT);
         updateLocale(args.getString(EXTRA_LOCALE));
     }
 
@@ -110,7 +121,8 @@
         // If we are in add mode, nothing was added, so we don't need to do anything.
     }
 
-    /* package */ int apply(final Context context, final Bundle outParameters) {
+    /* package */
+    int apply(final Context context, final Bundle outParameters) {
         if (null != outParameters) saveStateIntoBundle(outParameters);
         final ContentResolver resolver = context.getContentResolver();
         if (MODE_EDIT == mMode && !TextUtils.isEmpty(mOldWord)) {
@@ -119,7 +131,9 @@
         }
         final String newWord = mWordEditText.getText().toString();
         final String newShortcut;
-        if (null == mShortcutEditText) {
+        if (!UserDictionarySettings.IS_SHORTCUT_API_SUPPORTED) {
+            newShortcut = null;
+        } else if (null == mShortcutEditText) {
             newShortcut = null;
         } else {
             final String tmpShortcut = mShortcutEditText.getText().toString();
@@ -150,9 +164,9 @@
 
         // In this class we use the empty string to represent 'all locales' and mLocale cannot
         // be null. However the addWord method takes null to mean 'all locales'.
-        UserDictionary.Words.addWord(context, newWord.toString(),
-                FREQUENCY_FOR_USER_DICTIONARY_ADDS, newShortcut,
-                TextUtils.isEmpty(mLocale) ? null : LocaleUtils.constructLocaleFromString(mLocale));
+        UserDictionaryCompatUtils.addWord(context, newWord.toString(),
+                FREQUENCY_FOR_USER_DICTIONARY_ADDS, newShortcut, TextUtils.isEmpty(mLocale) ?
+                        null : LocaleUtils.constructLocaleFromString(mLocale));
 
         return CODE_WORD_ADDED;
     }
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java
index 7970a36..5f4c446 100644
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java
@@ -49,7 +49,8 @@
 public class UserDictionaryAddWordFragment extends Fragment
         implements AdapterView.OnItemSelectedListener, LocationChangedListener {
 
-    private static final int OPTIONS_MENU_DELETE = Menu.FIRST;
+    private static final int OPTIONS_MENU_ADD = Menu.FIRST;
+    private static final int OPTIONS_MENU_DELETE = Menu.FIRST + 1;
 
     private UserDictionaryAddWordContents mContents;
     private View mRootView;
@@ -73,21 +74,29 @@
 
     @Override
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        MenuItem actionItem = menu.add(0, OPTIONS_MENU_DELETE, 0,
+        final MenuItem actionItemDelete = menu.add(0, OPTIONS_MENU_DELETE, 0,
                 R.string.user_dict_settings_delete).setIcon(android.R.drawable.ic_menu_delete);
-        actionItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM |
-                MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+        actionItemDelete.setShowAsAction(
+                MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+        final MenuItem actionItemAdd = menu.add(0, OPTIONS_MENU_ADD, 0,
+                R.string.user_dict_settings_delete).setIcon(R.drawable.ic_menu_add);
+        actionItemAdd.setShowAsAction(
+                MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
     }
 
     /**
      * Callback for the framework when a menu option is pressed.
      *
-     * This class only supports the delete menu item.
      * @param MenuItem the item that was pressed
      * @return false to allow normal menu processing to proceed, true to consume it here
      */
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == OPTIONS_MENU_ADD) {
+            // added the entry in "onPause"
+            getActivity().onBackPressed();
+            return true;
+        }
         if (item.getItemId() == OPTIONS_MENU_DELETE) {
             mContents.delete(getActivity());
             mIsDeleting = true;
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java
index 2d147aa..6e64882 100644
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java
@@ -27,6 +27,7 @@
 import android.preference.PreferenceFragment;
 import android.preference.PreferenceGroup;
 import android.provider.UserDictionary;
+import android.text.TextUtils;
 
 import java.util.Locale;
 import java.util.TreeSet;
@@ -52,16 +53,26 @@
                 new String[] { UserDictionary.Words.LOCALE },
                 null, null, null);
         final TreeSet<String> localeList = new TreeSet<String>();
+        boolean addedAllLocale = false;
         if (null == cursor) {
             // The user dictionary service is not present or disabled. Return null.
             return null;
         } else if (cursor.moveToFirst()) {
             final int columnIndex = cursor.getColumnIndex(UserDictionary.Words.LOCALE);
             do {
-                String locale = cursor.getString(columnIndex);
-                localeList.add(null != locale ? locale : "");
+                final String locale = cursor.getString(columnIndex);
+                final boolean allLocale = TextUtils.isEmpty(locale);
+                localeList.add(allLocale ? "" : locale);
+                if (allLocale) {
+                    addedAllLocale = true;
+                }
             } while (cursor.moveToNext());
         }
+        if (!UserDictionarySettings.IS_SHORTCUT_API_SUPPORTED && !addedAllLocale) {
+            // For ICS, we need to show "For all languages" in case that the keyboard locale
+            // is different from the system locale
+            localeList.add("");
+        }
         localeList.add(Locale.getDefault().toString());
         return localeList;
     }
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java
index a250c24..36bc5ba 100644
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.database.Cursor;
+import android.os.Build;
 import android.os.Bundle;
 import android.provider.UserDictionary;
 import android.text.TextUtils;
@@ -47,13 +48,42 @@
 
 public class UserDictionarySettings extends ListFragment {
 
-    private static final String[] QUERY_PROJECTION = {
-        UserDictionary.Words._ID, UserDictionary.Words.WORD, UserDictionary.Words.SHORTCUT
-    };
+    public static final boolean IS_SHORTCUT_API_SUPPORTED =
+            Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
+
+    private static final String[] QUERY_PROJECTION_SHORTCUT_UNSUPPORTED =
+            { UserDictionary.Words._ID, UserDictionary.Words.WORD};
+    private static final String[] QUERY_PROJECTION_SHORTCUT_SUPPORTED =
+            { UserDictionary.Words._ID, UserDictionary.Words.WORD, UserDictionary.Words.SHORTCUT};
+    private static final String[] QUERY_PROJECTION =
+            IS_SHORTCUT_API_SUPPORTED ?
+                    QUERY_PROJECTION_SHORTCUT_SUPPORTED : QUERY_PROJECTION_SHORTCUT_UNSUPPORTED;
 
     // The index of the shortcut in the above array.
     private static final int INDEX_SHORTCUT = 2;
 
+    private static final String[] ADAPTER_FROM_SHORTCUT_UNSUPPORTED = {
+        UserDictionary.Words.WORD,
+    };
+
+    private static final String[] ADAPTER_FROM_SHORTCUT_SUPPORTED = {
+        UserDictionary.Words.WORD, UserDictionary.Words.SHORTCUT
+    };
+
+    private static final String[] ADAPTER_FROM = IS_SHORTCUT_API_SUPPORTED ?
+            ADAPTER_FROM_SHORTCUT_SUPPORTED : ADAPTER_FROM_SHORTCUT_UNSUPPORTED;
+
+    private static final int[] ADAPTER_TO_SHORTCUT_UNSUPPORTED = {
+        android.R.id.text1,
+    };
+
+    private static final int[] ADAPTER_TO_SHORTCUT_SUPPORTED = {
+        android.R.id.text1, android.R.id.text2
+    };
+
+    private static final int[] ADAPTER_TO = IS_SHORTCUT_API_SUPPORTED ?
+            ADAPTER_TO_SHORTCUT_SUPPORTED : ADAPTER_TO_SHORTCUT_UNSUPPORTED;
+
     // Either the locale is empty (means the word is applicable to all locales)
     // or the word equals our current locale
     private static final String QUERY_SELECTION =
@@ -66,6 +96,8 @@
     private static final String DELETE_SELECTION_WITHOUT_SHORTCUT = UserDictionary.Words.WORD
             + "=? AND " + UserDictionary.Words.SHORTCUT + " is null OR "
             + UserDictionary.Words.SHORTCUT + "=''";
+    private static final String DELETE_SELECTION_SHORTCUT_UNSUPPORTED =
+            UserDictionary.Words.WORD + "=?";
 
     private static final int OPTIONS_MENU_ADD = Menu.FIRST;
 
@@ -146,10 +178,8 @@
     }
 
     private ListAdapter createAdapter() {
-        return new MyAdapter(getActivity(),
-                R.layout.user_dictionary_item, mCursor,
-                new String[] { UserDictionary.Words.WORD, UserDictionary.Words.SHORTCUT },
-                new int[] { android.R.id.text1, android.R.id.text2 }, this);
+        return new MyAdapter(getActivity(), R.layout.user_dictionary_item, mCursor,
+                ADAPTER_FROM, ADAPTER_TO, this);
     }
 
     @Override
@@ -163,11 +193,20 @@
 
     @Override
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        if (!UserDictionarySettings.IS_SHORTCUT_API_SUPPORTED) {
+            final Locale systemLocale = getResources().getConfiguration().locale;
+            if (!TextUtils.isEmpty(mLocale) && !mLocale.equals(systemLocale.toString())) {
+                // Hide the add button for ICS because it doesn't support specifying a locale
+                // for an entry. This new "locale"-aware API has been added in conjunction
+                // with the shortcut API.
+                return;
+            }
+        }
         MenuItem actionItem =
                 menu.add(0, OPTIONS_MENU_ADD, 0, R.string.user_dict_settings_add_menu_title)
                 .setIcon(R.drawable.ic_menu_add);
-        actionItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM |
-                MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+        actionItem.setShowAsAction(
+                MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
     }
 
     @Override
@@ -209,6 +248,7 @@
     }
 
     private String getShortcut(final int position) {
+        if (!IS_SHORTCUT_API_SUPPORTED) return null;
         if (null == mCursor) return null;
         mCursor.moveToPosition(position);
         // Handle a possible race-condition
@@ -220,7 +260,10 @@
 
     public static void deleteWord(final String word, final String shortcut,
             final ContentResolver resolver) {
-        if (TextUtils.isEmpty(shortcut)) {
+        if (!IS_SHORTCUT_API_SUPPORTED) {
+            resolver.delete(UserDictionary.Words.CONTENT_URI, DELETE_SELECTION_SHORTCUT_UNSUPPORTED,
+                    new String[] { word });
+        } else if (TextUtils.isEmpty(shortcut)) {
             resolver.delete(
                     UserDictionary.Words.CONTENT_URI, DELETE_SELECTION_WITHOUT_SHORTCUT,
                     new String[] { word });
@@ -239,6 +282,10 @@
 
             @Override
             public boolean setViewValue(View v, Cursor c, int columnIndex) {
+                if (!IS_SHORTCUT_API_SUPPORTED) {
+                    // just let SimpleCursorAdapter set the view values
+                    return false;
+                }
                 if (columnIndex == INDEX_SHORTCUT) {
                     final String shortcut = c.getString(INDEX_SHORTCUT);
                     if (TextUtils.isEmpty(shortcut)) {