com.anysoftkeyboard.ui.settings.wordseditor.UserDictionaryEditorFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.anysoftkeyboard.ui.settings.wordseditor.UserDictionaryEditorFragment.java

Source

/*
 * Copyright (c) 2013 Menny Even-Danan
 *
 * 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.anysoftkeyboard.ui.settings.wordseditor;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBar;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.Spinner;

import com.anysoftkeyboard.dictionaries.EditableDictionary;
import com.anysoftkeyboard.dictionaries.UserDictionary;
import com.anysoftkeyboard.dictionaries.WordsCursor;
import com.anysoftkeyboard.keyboards.KeyboardAddOnAndBuilder;
import com.anysoftkeyboard.keyboards.KeyboardFactory;
import com.anysoftkeyboard.utils.Log;
import com.menny.android.anysoftkeyboard.AnyApplication;
import com.menny.android.anysoftkeyboard.R;

import net.evendanan.pushingpixels.AsyncTaskWithProgressWindow;
import net.evendanan.pushingpixels.FragmentChauffeurActivity;
import net.evendanan.pushingpixels.PassengerFragmentSupport;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class UserDictionaryEditorFragment extends Fragment implements AsyncTaskWithProgressWindow.AsyncTaskOwner,
        AdapterView.OnItemClickListener, UserWordsListAdapter.AdapterCallbacks {

    private Dialog mDialog;

    private static final String ASK_USER_WORDS_SDCARD_FILENAME = "UserWords.xml";

    static final int DIALOG_SAVE_SUCCESS = 10;
    static final int DIALOG_SAVE_FAILED = 11;

    static final int DIALOG_LOAD_SUCCESS = 20;
    static final int DIALOG_LOAD_FAILED = 21;

    static final String TAG = "ASK_UDE";

    Spinner mLanguagesSpinner;

    WordsCursor mCursor;
    private String mSelectedLocale = null;
    EditableDictionary mCurrentDictionary;

    AbsListView mWordsListView;//this may be either ListView or GridView (in tablets)
    private static final Comparator<UserWordsListAdapter.Word> msWordsComparator = new Comparator<UserWordsListAdapter.Word>() {
        @Override
        public int compare(UserWordsListAdapter.Word lhs, UserWordsListAdapter.Word rhs) {
            return lhs.word.compareTo(rhs.word);
        }
    };

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        setHasOptionsMenu(true);
        FragmentChauffeurActivity activity = (FragmentChauffeurActivity) getActivity();
        ActionBar actionBar = activity.getSupportActionBar();
        actionBar.setDisplayShowCustomEnabled(true);
        actionBar.setDisplayShowTitleEnabled(false);
        View v = inflater.inflate(R.layout.words_editor_actionbar_view, null);
        mLanguagesSpinner = (Spinner) v.findViewById(R.id.user_dictionay_langs);
        actionBar.setCustomView(v);

        return inflater.inflate(R.layout.user_dictionary_editor, container, false);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mLanguagesSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
            public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
                mSelectedLocale = ((DictionaryLocale) arg0.getItemAtPosition(arg2)).getLocale();
                fillWordsList();
            }

            public void onNothingSelected(AdapterView<?> arg0) {
                Log.d(TAG, "No locale selected");
                mSelectedLocale = null;
            }
        });

        View emptyView = view.findViewById(R.id.empty_user_dictionary);
        emptyView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                createEmptyItemForAdd();
            }
        });

        mWordsListView = (AbsListView) view.findViewById(android.R.id.list);
        mWordsListView.setFastScrollEnabled(true);
        //this is for the "empty state" - it will allow the user to quickly add the first word.
        mWordsListView.setEmptyView(emptyView);
        mWordsListView.setOnItemClickListener(this);
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        // Inflate the menu items for use in the action bar
        inflater.inflate(R.menu.words_editor_menu_actions, menu);
        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        if (item.getItemId() == R.id.add_user_word) {
            createEmptyItemForAdd();
            return true;
        } else if (item.getItemId() == R.id.backup_words) {
            new BackupUserWordsAsyncTask(UserDictionaryEditorFragment.this, ASK_USER_WORDS_SDCARD_FILENAME)
                    .execute();
            return true;
        } else if (item.getItemId() == R.id.restore_words) {
            new RestoreUserWordsAsyncTask(UserDictionaryEditorFragment.this, ASK_USER_WORDS_SDCARD_FILENAME)
                    .execute();
            return true;
        } else {
            return super.onOptionsItemSelected(item);
        }

    }

    private void createEmptyItemForAdd() {
        UserWordsListAdapter adapter = (UserWordsListAdapter) mWordsListView.getAdapter();
        final int addWordItemIndex = adapter.getCount() == 0 ? 0 : adapter.getCount() - 1;
        //will use smooth scrolling on API8+
        AnyApplication.getDeviceSpecific().performListScrollToPosition(mWordsListView, addWordItemIndex);
        onItemClick(mWordsListView, null, addWordItemIndex, 0l);
    }

    @Override
    public void onStart() {
        super.onStart();
        PassengerFragmentSupport.setActivityTitle(this, getString(R.string.user_dict_settings_titlebar));
        fillLanguagesSpinner();
    }

    @Override
    public void onDestroy() {
        FragmentChauffeurActivity activity = (FragmentChauffeurActivity) getActivity();
        ActionBar actionBar = activity.getSupportActionBar();
        actionBar.setDisplayShowCustomEnabled(false);
        actionBar.setDisplayShowTitleEnabled(true);
        actionBar.setCustomView(null);

        if (mDialog != null && mDialog.isShowing())
            mDialog.dismiss();
        mDialog = null;

        super.onDestroy();
        if (mCursor != null)
            mCursor.close();
        if (mCurrentDictionary != null)
            mCurrentDictionary.close();

        mCursor = null;
        mCurrentDictionary = null;
    }

    void fillLanguagesSpinner() {
        new UserWordsEditorAsyncTask(this) {
            private ArrayAdapter<DictionaryLocale> mAdapter;

            @Override
            protected void onPreExecute() {
                super.onPreExecute();
                //creating in the UI thread
                mAdapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_spinner_item);
            }

            @Override
            protected Void doAsyncTask(Void[] params) throws Exception {
                ArrayList<DictionaryLocale> languagesList = new ArrayList<>();

                ArrayList<KeyboardAddOnAndBuilder> keyboards = KeyboardFactory
                        .getEnabledKeyboards(getActivity().getApplicationContext());
                for (KeyboardAddOnAndBuilder kbd : keyboards) {
                    String locale = kbd.getKeyboardLocale();
                    if (TextUtils.isEmpty(locale))
                        continue;

                    DictionaryLocale dictionaryLocale = new DictionaryLocale(locale, kbd.getName());
                    //Don't worry, DictionaryLocale equals any DictionaryLocale with the same locale (no matter what its name is)
                    if (languagesList.contains(dictionaryLocale))
                        continue;
                    Log.d(TAG, "Adding locale " + locale + " to editor.");
                    languagesList.add(dictionaryLocale);
                }

                mAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
                for (DictionaryLocale lang : languagesList)
                    mAdapter.add(lang);

                return null;
            }

            @Override
            protected void applyResults(Void result, Exception backgroundException) {
                mLanguagesSpinner.setAdapter(mAdapter);
            }
        }.execute();
    }

    public void showDialog(int id) {
        mDialog = onCreateDialog(id);
        mDialog.show();
    }

    private Dialog onCreateDialog(int id) {
        switch (id) {
        case DIALOG_SAVE_SUCCESS:
            return createDialogAlert(R.string.user_dict_backup_success_title,
                    R.string.user_dict_backup_success_text);
        case DIALOG_SAVE_FAILED:
            return createDialogAlert(R.string.user_dict_backup_fail_title, R.string.user_dict_backup_fail_text);
        case DIALOG_LOAD_SUCCESS:
            return createDialogAlert(R.string.user_dict_restore_success_title,
                    R.string.user_dict_restore_success_text);
        case DIALOG_LOAD_FAILED:
            return createDialogAlert(R.string.user_dict_restore_fail_title, R.string.user_dict_restore_fail_text);
        }

        return null;
    }

    private Dialog createDialogAlert(int title, int text) {
        return new AlertDialog.Builder(getActivity()).setTitle(title).setMessage(text)
                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                }).create();
    }

    private void deleteWord(String word) {
        mCurrentDictionary.deleteWord(word);
    }

    private void fillWordsList() {
        Log.d(TAG, "Selected locale is " + mSelectedLocale);
        new UserWordsEditorAsyncTask(this) {
            private EditableDictionary mNewDictionary;
            private List<UserWordsListAdapter.Word> mWordsList;

            @Override
            protected void onPreExecute() {
                super.onPreExecute();
                // all the code below can be safely (and must) be called in the
                // UI thread.
                mNewDictionary = getEditableDictionary(mSelectedLocale);
                if (mNewDictionary != mCurrentDictionary && mCurrentDictionary != null && mCursor != null) {
                    mCurrentDictionary.close();
                }
            }

            @Override
            protected Void doAsyncTask(Void[] params) throws Exception {
                mCurrentDictionary = mNewDictionary;
                mCurrentDictionary.loadDictionary();
                mCursor = mCurrentDictionary.getWordsCursor();
                Cursor cursor = mCursor.getCursor();
                mWordsList = new ArrayList<>(mCursor.getCursor().getCount());
                cursor.moveToFirst();
                while (!cursor.isAfterLast()) {
                    UserWordsListAdapter.Word word = new UserWordsListAdapter.Word(mCursor.getCurrentWord(),
                            mCursor.getCurrentWordFrequency());
                    mWordsList.add(word);
                    cursor.moveToNext();
                }
                //now, sorting the word list alphabetically
                Collections.sort(mWordsList, msWordsComparator);
                return null;
            }

            protected void applyResults(Void result, Exception backgroundException) {
                ListAdapter adapter = getWordsListAdapter(mWordsList);
                //AbsListView introduced the setAdapter method in API11, so I'm required to check the instance type
                if (mWordsListView instanceof ListView) {
                    //this is NOT a redundant cast!
                    ((ListView) mWordsListView).setAdapter(adapter);
                } else if (mWordsListView instanceof GridView) {
                    //this is NOT a redundant cast!
                    ((GridView) mWordsListView).setAdapter(adapter);
                } else {
                    throw new ClassCastException("Unknown mWordsListView type " + mWordsListView.getClass());
                }
            }
        }.execute();
    }

    protected ListAdapter getWordsListAdapter(List<UserWordsListAdapter.Word> wordsList) {
        return new UserWordsListAdapter(getActivity(), wordsList, this);
    }

    protected EditableDictionary getEditableDictionary(String locale) {
        return new UserDictionary(getActivity().getApplicationContext(), locale);
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        ((UserWordsListAdapter) mWordsListView.getAdapter()).onItemClicked(parent, position);
    }

    @Override
    public void onWordDeleted(final UserWordsListAdapter.Word word) {
        new UserWordsEditorAsyncTask(this) {
            @Override
            protected Void doAsyncTask(Void[] params) throws Exception {
                deleteWord(word.word);
                return null;
            }

            @Override
            protected void applyResults(Void aVoid, Exception backgroundException) {
                fillWordsList();
            }
        }.execute();
    }

    @Override
    public void onWordUpdated(final String oldWord, final UserWordsListAdapter.Word newWord) {

        new UserWordsEditorAsyncTask(this) {
            @Override
            protected Void doAsyncTask(Void[] params) throws Exception {
                if (!TextUtils.isEmpty(oldWord))//it can be empty in case it's a new word.
                    deleteWord(oldWord);
                deleteWord(newWord.word);
                mCurrentDictionary.addWord(newWord.word, newWord.frequency);
                return null;
            }

            @Override
            protected void applyResults(Void aVoid, Exception backgroundException) {
                fillWordsList();
            }
        }.execute();
    }

    @Override
    public void performDiscardEdit() {
        ((UserWordsListAdapter) mWordsListView.getAdapter()).onItemClicked(mWordsListView,
                -1/*doesn't really matter what position it is*/);
    }
}