From 8c2591eec80dbe44158b1613cd96056057a41139 Mon Sep 17 00:00:00 2001 From: Jatin Matani Date: Wed, 1 Jul 2015 13:54:44 -0700 Subject: [PATCH] Bring back shortcuts and add to dictionary UI For JB and lower devices, the UI is surfaced by the IME. Bug: 22200135 Change-Id: Icca08500ee0683e2ceb5357b0bc430cd1712220e --- .../user_dictionary_add_word_fullscreen.xml | 22 +- .../compat/UserDictionaryCompatUtils.java | 49 +++++ .../UserDictionaryAddWordContents.java | 78 +++++-- .../UserDictionaryAddWordFragment.java | 179 ++++++++++++++++ .../userdictionary/UserDictionaryList.java | 4 +- .../UserDictionarySettings.java | 194 +++++++++++++----- .../latin/utils/FragmentUtils.java | 2 + 7 files changed, 466 insertions(+), 62 deletions(-) create mode 100644 java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java create mode 100644 java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java diff --git a/java/res/layout/user_dictionary_add_word_fullscreen.xml b/java/res/layout/user_dictionary_add_word_fullscreen.xml index cbdfba67d..baaaf7e6d 100644 --- a/java/res/layout/user_dictionary_add_word_fullscreen.xml +++ b/java/res/layout/user_dictionary_add_word_fullscreen.xml @@ -43,6 +43,26 @@ android:layout_marginStart="8dip" android:columnCount="2" > + + + + - \ No newline at end of file + diff --git a/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java b/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java new file mode 100644 index 000000000..3633d667d --- /dev/null +++ b/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.compat; + +import android.annotation.TargetApi; +import android.content.Context; +import android.os.Build; +import android.provider.UserDictionary; + +import java.util.Locale; + +public final class UserDictionaryCompatUtils { + @SuppressWarnings("deprecation") + public static void addWord(final Context context, final String word, + final int freq, final String shortcut, final Locale locale) { + if (BuildCompatUtils.EFFECTIVE_SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + addWordWithShortcut(context, word, freq, shortcut, locale); + return; + } + // Fall back to the pre-JellyBean method. + final Locale currentLocale = context.getResources().getConfiguration().locale; + final int localeType = currentLocale.equals(locale) + ? UserDictionary.Words.LOCALE_TYPE_CURRENT : UserDictionary.Words.LOCALE_TYPE_ALL; + UserDictionary.Words.addWord(context, word, freq, localeType); + } + + // {@link UserDictionary.Words#addWord(Context,String,int,String,Locale)} was introduced + // in API level 16 (Build.VERSION_CODES.JELLY_BEAN). + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + private static void addWordWithShortcut(final Context context, final String word, + final int freq, final String shortcut, final Locale locale) { + UserDictionary.Words.addWord(context, word, freq, shortcut, locale); + } +} + diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java index cb615f3af..58e6a25b8 100644 --- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java +++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java @@ -26,6 +26,7 @@ import android.text.TextUtils; import android.view.View; import android.widget.EditText; +import com.android.inputmethod.compat.UserDictionaryCompatUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.common.LocaleUtils; @@ -46,7 +47,10 @@ import javax.annotation.Nullable; public class UserDictionaryAddWordContents { public static final String EXTRA_MODE = "mode"; public static final String EXTRA_WORD = "word"; + public static final String EXTRA_SHORTCUT = "shortcut"; public static final String EXTRA_LOCALE = "locale"; + public static final String EXTRA_ORIGINAL_WORD = "originalWord"; + public static final String EXTRA_ORIGINAL_SHORTCUT = "originalShortcut"; public static final int MODE_EDIT = 0; public static final int MODE_INSERT = 1; @@ -59,12 +63,20 @@ public class UserDictionaryAddWordContents { private final int mMode; // Either MODE_EDIT or MODE_INSERT private final EditText mWordEditText; + private final EditText mShortcutEditText; private String mLocale; private final String mOldWord; + private final String mOldShortcut; private String mSavedWord; + private String mSavedShortcut; /* 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); @@ -72,6 +84,17 @@ public class UserDictionaryAddWordContents { // it's too long to be edited. mWordEditText.setSelection(mWordEditText.getText().length()); } + 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); updateLocale(args.getString(EXTRA_LOCALE)); @@ -80,8 +103,10 @@ public class UserDictionaryAddWordContents { /* package */ UserDictionaryAddWordContents(final View view, final UserDictionaryAddWordContents oldInstanceToBeEdited) { mWordEditText = (EditText)view.findViewById(R.id.user_dictionary_add_word_text); + mShortcutEditText = (EditText)view.findViewById(R.id.user_dictionary_add_shortcut); mMode = MODE_EDIT; mOldWord = oldInstanceToBeEdited.mSavedWord; + mOldShortcut = oldInstanceToBeEdited.mSavedShortcut; updateLocale(mLocale); } @@ -93,6 +118,13 @@ public class UserDictionaryAddWordContents { /* package */ void saveStateIntoBundle(final Bundle outState) { outState.putString(EXTRA_WORD, mWordEditText.getText().toString()); + outState.putString(EXTRA_ORIGINAL_WORD, mOldWord); + if (null != mShortcutEditText) { + outState.putString(EXTRA_SHORTCUT, mShortcutEditText.getText().toString()); + } + if (null != mOldShortcut) { + outState.putString(EXTRA_ORIGINAL_SHORTCUT, mOldShortcut); + } outState.putString(EXTRA_LOCALE, mLocale); } @@ -100,7 +132,7 @@ public class UserDictionaryAddWordContents { if (MODE_EDIT == mMode && !TextUtils.isEmpty(mOldWord)) { // Mode edit: remove the old entry. final ContentResolver resolver = context.getContentResolver(); - UserDictionarySettings.deleteWord(mOldWord, resolver); + UserDictionarySettings.deleteWord(mOldWord, mOldShortcut, resolver); } // If we are in add mode, nothing was added, so we don't need to do anything. } @@ -111,31 +143,50 @@ public class UserDictionaryAddWordContents { final ContentResolver resolver = context.getContentResolver(); if (MODE_EDIT == mMode && !TextUtils.isEmpty(mOldWord)) { // Mode edit: remove the old entry. - UserDictionarySettings.deleteWord(mOldWord, resolver); + UserDictionarySettings.deleteWord(mOldWord, mOldShortcut, resolver); } final String newWord = mWordEditText.getText().toString(); + final String newShortcut; + if (!UserDictionarySettings.IS_SHORTCUT_API_SUPPORTED) { + newShortcut = null; + } else if (null == mShortcutEditText) { + newShortcut = null; + } else { + final String tmpShortcut = mShortcutEditText.getText().toString(); + if (TextUtils.isEmpty(tmpShortcut)) { + newShortcut = null; + } else { + newShortcut = tmpShortcut; + } + } if (TextUtils.isEmpty(newWord)) { // If the word is somehow empty, don't insert it. return CODE_CANCEL; } mSavedWord = newWord; - // If the word already exists in the database, then we should not insert. - if (hasWord(newWord, context)) { + mSavedShortcut = newShortcut; + // If there is no shortcut, and the word already exists in the database, then we + // should not insert, because either A. the word exists with no shortcut, in which + // case the exact same thing we want to insert is already there, or B. the word + // exists with at least one shortcut, in which case it has priority on our word. + if (TextUtils.isEmpty(newShortcut) && hasWord(newWord, context)) { return CODE_ALREADY_PRESENT; } - // Disallow duplicates. If the same word is defined, remove it. - UserDictionarySettings.deleteWord(newWord, resolver); + // Disallow duplicates. If the same word with no shortcut is defined, remove it; if + // the same word with the same shortcut is defined, remove it; but we don't mind if + // there is the same word with a different, non-empty shortcut. + UserDictionarySettings.deleteWord(newWord, null, resolver); + if (!TextUtils.isEmpty(newShortcut)) { + // If newShortcut is empty we just deleted this, no need to do it again + UserDictionarySettings.deleteWord(newWord, newShortcut, resolver); + } // 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'. - final Locale locale = TextUtils.isEmpty(mLocale) ? - null : LocaleUtils.constructLocaleFromString(mLocale); - final Locale currentLocale = context.getResources().getConfiguration().locale; - final boolean useCurrentLocale = currentLocale.equals(locale); - UserDictionary.Words.addWord(context, newWord.toString(), - FREQUENCY_FOR_USER_DICTIONARY_ADDS, null /* shortcut */, - useCurrentLocale ? Locale.getDefault() : null); + UserDictionaryCompatUtils.addWord(context, newWord.toString(), + FREQUENCY_FOR_USER_DICTIONARY_ADDS, newShortcut, TextUtils.isEmpty(mLocale) ? + null : LocaleUtils.constructLocaleFromString(mLocale)); return CODE_WORD_ADDED; } @@ -232,3 +283,4 @@ public class UserDictionaryAddWordContents { return mLocale; } } + diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java new file mode 100644 index 000000000..a8781d786 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.userdictionary; + +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.userdictionary.UserDictionaryAddWordContents.LocaleRenderer; +import com.android.inputmethod.latin.userdictionary.UserDictionaryLocalePicker.LocationChangedListener; + +import android.app.Fragment; +import android.os.Bundle; +import android.preference.PreferenceActivity; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Spinner; + +import java.util.ArrayList; +import java.util.Locale; + +// Caveat: This class is basically taken from +// packages/apps/Settings/src/com/android/settings/inputmethod/UserDictionaryAddWordFragment.java +// in order to deal with some devices that have issues with the user dictionary handling + +/** + * Fragment to add a word/shortcut to the user dictionary. + * + * As opposed to the UserDictionaryActivity, this is only invoked within Settings + * from the UserDictionarySettings. + */ +public class UserDictionaryAddWordFragment extends Fragment + implements AdapterView.OnItemSelectedListener, LocationChangedListener { + + 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; + private boolean mIsDeleting = false; + + @Override + public void onActivityCreated(final Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + setHasOptionsMenu(true); + getActivity().getActionBar().setTitle(R.string.edit_personal_dictionary); + // Keep the instance so that we remember mContents when configuration changes (eg rotation) + setRetainInstance(true); + } + + @Override + public View onCreateView(final LayoutInflater inflater, final ViewGroup container, + final Bundle savedState) { + mRootView = inflater.inflate(R.layout.user_dictionary_add_word_fullscreen, null); + mIsDeleting = false; + // If we have a non-null mContents object, it's the old value before a configuration + // change (eg rotation) so we need to use its values. Otherwise, read from the arguments. + if (null == mContents) { + mContents = new UserDictionaryAddWordContents(mRootView, getArguments()); + } else { + // We create a new mContents object to account for the new situation : a word has + // been added to the user dictionary when we started rotating, and we are now editing + // it. That means in particular if the word undergoes any change, the old version should + // be updated, so the mContents object needs to switch to EDIT mode if it was in + // INSERT mode. + mContents = new UserDictionaryAddWordContents(mRootView, + mContents /* oldInstanceToBeEdited */); + } + getActivity().getActionBar().setSubtitle(UserDictionarySettingsUtils.getLocaleDisplayName( + getActivity(), mContents.getCurrentUserDictionaryLocale())); + return mRootView; + } + + @Override + public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { + final MenuItem actionItemAdd = menu.add(0, OPTIONS_MENU_ADD, 0, + R.string.user_dict_settings_add_menu_title).setIcon(R.drawable.ic_menu_add); + actionItemAdd.setShowAsAction( + MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + final MenuItem actionItemDelete = menu.add(0, OPTIONS_MENU_DELETE, 0, + R.string.user_dict_settings_delete).setIcon(android.R.drawable.ic_menu_delete); + actionItemDelete.setShowAsAction( + MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + } + + /** + * Callback for the framework when a menu option is pressed. + * + * @param item 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; + getActivity().onBackPressed(); + return true; + } + return false; + } + + @Override + public void onResume() { + super.onResume(); + // We are being shown: display the word + updateSpinner(); + } + + private void updateSpinner() { + final ArrayList localesList = mContents.getLocalesList(getActivity()); + + final Spinner localeSpinner = + (Spinner)mRootView.findViewById(R.id.user_dictionary_add_locale); + final ArrayAdapter adapter = new ArrayAdapter<>( + getActivity(), android.R.layout.simple_spinner_item, localesList); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + localeSpinner.setAdapter(adapter); + localeSpinner.setOnItemSelectedListener(this); + } + + @Override + public void onPause() { + super.onPause(); + // We are being hidden: commit changes to the user dictionary, unless we were deleting it + if (!mIsDeleting) { + mContents.apply(getActivity(), null); + } + } + + @Override + public void onItemSelected(final AdapterView parent, final View view, final int pos, + final long id) { + final LocaleRenderer locale = (LocaleRenderer)parent.getItemAtPosition(pos); + if (locale.isMoreLanguages()) { + PreferenceActivity preferenceActivity = (PreferenceActivity)getActivity(); + preferenceActivity.startPreferenceFragment(new UserDictionaryLocalePicker(), true); + } else { + mContents.updateLocale(locale.getLocaleString()); + } + } + + @Override + public void onNothingSelected(final AdapterView parent) { + // I'm not sure we can come here, but if we do, that's the right thing to do. + final Bundle args = getArguments(); + mContents.updateLocale(args.getString(UserDictionaryAddWordContents.EXTRA_LOCALE)); + } + + // Called by the locale picker + @Override + public void onLocaleSelected(final Locale locale) { + mContents.updateLocale(locale.toString()); + getActivity().onBackPressed(); + } +} + diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java index 57347ce8c..f7940e3de 100644 --- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java +++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java @@ -20,7 +20,6 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; import android.database.Cursor; -import android.os.Build; import android.os.Bundle; import android.preference.Preference; import android.preference.PreferenceFragment; @@ -75,7 +74,7 @@ public class UserDictionaryList extends PreferenceFragment { } finally { cursor.close(); } - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { + if (!UserDictionarySettings.IS_SHORTCUT_API_SUPPORTED) { // For ICS, we need to show "For all languages" in case that the keyboard locale // is different from the system locale localeSet.add(""); @@ -163,3 +162,4 @@ public class UserDictionaryList extends PreferenceFragment { createUserDictSettings(getPreferenceScreen()); } } + diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java index bd3572340..d9339d965 100644 --- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java +++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java @@ -16,12 +16,6 @@ package com.android.inputmethod.latin.userdictionary; -import static com.android.inputmethod.latin.userdictionary.UserDictionaryAddWordContents.EXTRA_LOCALE; -import static com.android.inputmethod.latin.userdictionary.UserDictionaryAddWordContents.EXTRA_MODE; -import static com.android.inputmethod.latin.userdictionary.UserDictionaryAddWordContents.EXTRA_WORD; -import static com.android.inputmethod.latin.userdictionary.UserDictionaryAddWordContents.MODE_EDIT; -import static com.android.inputmethod.latin.userdictionary.UserDictionaryAddWordContents.MODE_INSERT; - import com.android.inputmethod.latin.R; import android.app.ListFragment; @@ -31,7 +25,7 @@ import android.content.Intent; import android.database.Cursor; import android.os.Build; import android.os.Bundle; -import android.provider.UserDictionary.Words; +import android.provider.UserDictionary; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.Menu; @@ -54,8 +48,62 @@ import java.util.Locale; public class UserDictionarySettings extends ListFragment { + 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 = + UserDictionary.Words.LOCALE + "=?"; + private static final String QUERY_SELECTION_ALL_LOCALES = + UserDictionary.Words.LOCALE + " is null"; + + private static final String DELETE_SELECTION_WITH_SHORTCUT = UserDictionary.Words.WORD + + "=? AND " + UserDictionary.Words.SHORTCUT + "=?"; + 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; + private Cursor mCursor; - private String mLocale; + + protected String mLocale; @Override public void onCreate(Bundle savedInstanceState) { @@ -112,6 +160,19 @@ public class UserDictionarySettings extends ListFragment { UserDictionarySettingsUtils.getLocaleDisplayName(getActivity(), mLocale)); } + @Override + public void onResume() { + super.onResume(); + ListAdapter adapter = getListView().getAdapter(); + if (adapter != null && adapter instanceof MyAdapter) { + // The list view is forced refreshed here. This allows the changes done + // in UserDictionaryAddWordFragment (update/delete/insert) to be seen when + // user goes back to this view. + MyAdapter listAdapter = (MyAdapter) adapter; + listAdapter.notifyDataSetChanged(); + } + } + @SuppressWarnings("deprecation") private Cursor createCursor(final String locale) { // Locale can be any of: @@ -124,60 +185,54 @@ public class UserDictionarySettings extends ListFragment { // TODO: it should be easy to make this more readable by making the special values // human-readable, like "all_locales" and "current_locales" strings, provided they // can be guaranteed not to match locales that may exist. - if (TextUtils.isEmpty(locale)) { + if ("".equals(locale)) { // Case-insensitive sort - return getActivity().managedQuery( - Words.CONTENT_URI, - new String[] { Words._ID, Words.WORD }, - Words.LOCALE + " is null", - null, - "UPPER(" + Words.WORD + ")"); + return getActivity().managedQuery(UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION, + QUERY_SELECTION_ALL_LOCALES, null, + "UPPER(" + UserDictionary.Words.WORD + ")"); } - return getActivity().managedQuery( - Words.CONTENT_URI, - new String[] { Words._ID, Words.WORD }, - Words.LOCALE + "=?", - new String[] { locale }, - "UPPER(" + Words.WORD + ")"); + final String queryLocale = null != locale ? locale : Locale.getDefault().toString(); + return getActivity().managedQuery(UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION, + QUERY_SELECTION, new String[] { queryLocale }, + "UPPER(" + UserDictionary.Words.WORD + ")"); } private ListAdapter createAdapter() { - return new MyAdapter( - getActivity(), - R.layout.user_dictionary_item, - mCursor, - new String[] { Words.WORD }, - new int[] { android.R.id.text1 }); + return new MyAdapter(getActivity(), R.layout.user_dictionary_item, mCursor, + ADAPTER_FROM, ADAPTER_TO); } @Override public void onListItemClick(ListView l, View v, int position, long id) { final String word = getWord(position); + final String shortcut = getShortcut(position); if (word != null) { - showAddOrEditDialog(word); + showAddOrEditDialog(word, shortcut); } } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { + 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. + // for an entry. This new "locale"-aware API has been added in conjunction + // with the shortcut API. return; } } - menu.add(0, Menu.FIRST, 0, R.string.user_dict_settings_add_menu_title) - .setIcon(R.drawable.ic_menu_add) - .setShowAsAction( - MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + 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); } @Override public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == Menu.FIRST) { - showAddOrEditDialog(null); + if (item.getItemId() == OPTIONS_MENU_ADD) { + showAddOrEditDialog(null, null); return true; } return false; @@ -186,13 +241,20 @@ public class UserDictionarySettings extends ListFragment { /** * Add or edit a word. If editingWord is null, it's an add; otherwise, it's an edit. * @param editingWord the word to edit, or null if it's an add. + * @param editingShortcut the shortcut for this entry, or null if none. */ - private void showAddOrEditDialog(final String editingWord) { + private void showAddOrEditDialog(final String editingWord, final String editingShortcut) { final Bundle args = new Bundle(); - args.putInt(EXTRA_MODE, editingWord == null ? MODE_INSERT : MODE_EDIT); - args.putString(EXTRA_WORD, editingWord); - args.putString(EXTRA_LOCALE, mLocale); - getActivity(); + args.putInt(UserDictionaryAddWordContents.EXTRA_MODE, null == editingWord + ? UserDictionaryAddWordContents.MODE_INSERT + : UserDictionaryAddWordContents.MODE_EDIT); + args.putString(UserDictionaryAddWordContents.EXTRA_WORD, editingWord); + args.putString(UserDictionaryAddWordContents.EXTRA_SHORTCUT, editingShortcut); + args.putString(UserDictionaryAddWordContents.EXTRA_LOCALE, mLocale); + android.preference.PreferenceActivity pa = + (android.preference.PreferenceActivity)getActivity(); + pa.startPreferencePanel(UserDictionaryAddWordFragment.class.getName(), + args, R.string.user_dict_settings_add_dialog_title, null, null, 0); } private String getWord(final int position) { @@ -201,11 +263,35 @@ public class UserDictionarySettings extends ListFragment { // Handle a possible race-condition if (mCursor.isAfterLast()) return null; - return mCursor.getString(mCursor.getColumnIndexOrThrow(Words.WORD)); + return mCursor.getString( + mCursor.getColumnIndexOrThrow(UserDictionary.Words.WORD)); } - public static void deleteWord(final String word, final ContentResolver resolver) { - resolver.delete(Words.CONTENT_URI, Words.WORD + "=?", new String[] { word }); + 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 + if (mCursor.isAfterLast()) return null; + + return mCursor.getString( + mCursor.getColumnIndexOrThrow(UserDictionary.Words.SHORTCUT)); + } + + public static void deleteWord(final String word, final String shortcut, + final ContentResolver resolver) { + 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 }); + } else { + resolver.delete( + UserDictionary.Words.CONTENT_URI, DELETE_SELECTION_WITH_SHORTCUT, + new String[] { word, shortcut }); + } } private static class MyAdapter extends SimpleCursorAdapter implements SectionIndexer { @@ -215,7 +301,22 @@ public class UserDictionarySettings extends ListFragment { @Override public boolean setViewValue(final View v, final Cursor c, final int columnIndex) { - // just let SimpleCursorAdapter set the view values + 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)) { + v.setVisibility(View.GONE); + } else { + ((TextView)v).setText(shortcut); + v.setVisibility(View.VISIBLE); + } + v.invalidate(); + return true; + } + return false; } }; @@ -226,7 +327,7 @@ public class UserDictionarySettings extends ListFragment { if (null != c) { final String alphabet = context.getString(R.string.user_dict_fast_scroll_alphabet); - final int wordColIndex = c.getColumnIndexOrThrow(Words.WORD); + final int wordColIndex = c.getColumnIndexOrThrow(UserDictionary.Words.WORD); mIndexer = new AlphabetIndexer(c, wordColIndex, alphabet); } setViewBinder(mViewBinder); @@ -248,3 +349,4 @@ public class UserDictionarySettings extends ListFragment { } } } + diff --git a/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java b/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java index c87a7c05c..147e57b13 100644 --- a/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java @@ -29,6 +29,7 @@ import com.android.inputmethod.latin.settings.PreferencesSettingsFragment; import com.android.inputmethod.latin.settings.SettingsFragment; import com.android.inputmethod.latin.settings.ThemeSettingsFragment; import com.android.inputmethod.latin.spellcheck.SpellCheckerSettingsFragment; +import com.android.inputmethod.latin.userdictionary.UserDictionaryAddWordFragment; import com.android.inputmethod.latin.userdictionary.UserDictionaryList; import com.android.inputmethod.latin.userdictionary.UserDictionaryLocalePicker; import com.android.inputmethod.latin.userdictionary.UserDictionarySettings; @@ -51,6 +52,7 @@ public class FragmentUtils { sLatinImeFragments.add(DebugSettingsFragment.class.getName()); sLatinImeFragments.add(SettingsFragment.class.getName()); sLatinImeFragments.add(SpellCheckerSettingsFragment.class.getName()); + sLatinImeFragments.add(UserDictionaryAddWordFragment.class.getName()); sLatinImeFragments.add(UserDictionaryList.class.getName()); sLatinImeFragments.add(UserDictionaryLocalePicker.class.getName()); sLatinImeFragments.add(UserDictionarySettings.class.getName());