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

Commit 8c2591ee authored by Jatin Matani's avatar Jatin Matani
Browse files

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
parent d23dd597
Loading
Loading
Loading
Loading
+21 −1
Original line number Diff line number Diff line
@@ -43,6 +43,26 @@
        android:layout_marginStart="8dip"
        android:columnCount="2" >

        <TextView
            android:id="@+id/user_dictionary_add_shortcut_label"
            style="?android:attr/textAppearanceSmall"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="start|center_vertical"
            android:text="@string/user_dict_settings_add_shortcut_option_name" />

        <EditText
            android:id="@+id/user_dictionary_add_shortcut"
            android:layout_width="wrap_content"
            android:layout_gravity="fill_horizontal|center_vertical"
            android:layout_marginBottom="8dip"
            android:layout_marginStart="8dip"
            android:layout_marginTop="8dip"
            android:hint="@string/user_dict_settings_add_shortcut_hint"
            android:imeOptions="flagNoFullscreen"
            android:inputType="textNoSuggestions"
            android:maxLength="@integer/config_user_dictionary_max_word_length" />

        <TextView
            android:id="@+id/user_dictionary_add_locale_label"
            style="?android:attr/textAppearanceSmall"
+49 −0
Original line number Diff line number Diff line
/*
 * 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);
    }
}
+65 −13
Original line number Diff line number Diff line
@@ -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;
    }
}
+179 −0
Original line number Diff line number Diff line
/*
 * 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<LocaleRenderer> localesList = mContents.getLocalesList(getActivity());

        final Spinner localeSpinner =
                (Spinner)mRootView.findViewById(R.id.user_dictionary_add_locale);
        final ArrayAdapter<LocaleRenderer> 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();
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -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());
    }
}
Loading