ca.rmen.android.poetassistant.main.dictionaries.search.Search.java Source code

Java tutorial

Introduction

Here is the source code for ca.rmen.android.poetassistant.main.dictionaries.search.Search.java

Source

/*
 * Copyright (c) 2016 Carmen Alvarez
 *
 * This file is part of Poet Assistant.
 *
 * Poet Assistant is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Poet Assistant is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Poet Assistant.  If not, see <http://www.gnu.org/licenses/>.
 */

package ca.rmen.android.poetassistant.main.dictionaries.search;

import android.app.Activity;
import android.app.SearchManager;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Build;
import android.support.annotation.MainThread;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import android.support.v4.app.Fragment;
import android.support.v4.view.ViewPager;
import android.support.v7.widget.SearchView;
import android.util.Log;
import android.view.ViewTreeObserver;

import java.util.Locale;

import javax.inject.Inject;

import ca.rmen.android.poetassistant.Constants;
import ca.rmen.android.poetassistant.DaggerHelper;
import ca.rmen.android.poetassistant.main.PagerAdapter;
import ca.rmen.android.poetassistant.main.Tab;
import ca.rmen.android.poetassistant.main.dictionaries.ResultListFragment;
import ca.rmen.android.poetassistant.main.dictionaries.dictionary.Dictionary;
import ca.rmen.android.poetassistant.main.dictionaries.dictionary.DictionaryEntry;

/**
 * Glue between the fragments, activity, and view pager, for executing searches.
 * <p/>
 * The activity calls this class to perform searches.  This class retrieves the fragments from
 * the Viewpager, and calls the fragments (which call the adapters) to perform the search and
 * display the search results.
 * <p/>
 * This class also configures the SearchView widget, and intercepts searches to add them to
 * the list of suggested words.
 */
public class Search {
    private static final String TAG = Constants.TAG + Search.class.getSimpleName();
    private final ViewPager mViewPager;
    private final PagerAdapter mPagerAdapter;
    private final Context mContext;
    @Inject
    Dictionary mDictionary;

    public Search(Activity searchableActivity, ViewPager viewPager) {
        DaggerHelper.getAppComponent(searchableActivity).inject(this);
        mContext = searchableActivity;
        mViewPager = viewPager;
        mPagerAdapter = (PagerAdapter) viewPager.getAdapter();
    }

    public void setSearchView(SearchView searchView) {
        SearchManager searchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
        ComponentName searchableActivityComponentName = new ComponentName(mContext, mContext.getClass());
        searchView.setSearchableInfo(searchManager.getSearchableInfo(searchableActivityComponentName));
    }

    /**
     * Search for the given word in the given dictionary, and set the current tab
     * to that dictionary (if it's not already the case).
     */
    public void search(String word, Tab tab) {
        Log.d(TAG, "search() called with: " + "word = [" + word + "], tab = [" + tab + "]");
        mViewPager.setCurrentItem(mPagerAdapter.getPositionForTab(tab));
        word = word.trim().toLowerCase(Locale.US);
        // Not intuitive: instantiateItem will actually return an existing Fragment, whereas getItem() will always instantiate a new Fragment.
        // We want to retrieve the existing fragment.
        ((ResultListFragment<?>) mPagerAdapter.getFragment(mViewPager, tab)).query(word);
    }

    /**
     * Search for the given word in all dictionaries
     */
    public void search(String word) {
        Log.d(TAG, "search() called with: " + "word = [" + word + "]");
        String wordTrimmed = word.trim().toLowerCase(Locale.US);

        selectTabForSearch(wordTrimmed);
        final Runnable performSearch = () -> {
            if (Patterns.isPattern(wordTrimmed)) {
                ((ResultListFragment<?>) mPagerAdapter.getFragment(mViewPager, Tab.PATTERN)).query(wordTrimmed);
            } else {
                ((ResultListFragment<?>) mPagerAdapter.getFragment(mViewPager, Tab.RHYMER)).query(wordTrimmed);
                ((ResultListFragment<?>) mPagerAdapter.getFragment(mViewPager, Tab.THESAURUS)).query(wordTrimmed);
                ((ResultListFragment<?>) mPagerAdapter.getFragment(mViewPager, Tab.DICTIONARY)).query(wordTrimmed);
            }
        };
        // Issue #19: In a specific scenario, the fragments may not be "ready" yet (onCreateView() may not have been called).
        // Wait until the ViewPager is laid out before invoking anything on the fragments.
        // (We assume that the fragments are "ready" once the ViewPager is laid out.)
        if (mViewPager.isShown()) {
            Log.d(TAG, "searching immediately");
            mViewPager.post(performSearch);
        } else {
            mViewPager.getViewTreeObserver()
                    .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                        @Override
                        public void onGlobalLayout() {
                            Log.d(TAG, "searching after layout");
                            performSearch.run();
                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                                mViewPager.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                            } else {
                                //noinspection deprecation
                                mViewPager.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                            }
                        }
                    });
        }
    }

    /**
     * Navigate to the appropriate tag for the search term:
     * If it's a pattern, open the pattern tab.
     * If it's any other word:
     *  - If we're in the reader tab, go to rhymer tab
     *  - Otherwise stay in the current tab
     */
    private void selectTabForSearch(String word) {
        final boolean isPattern = Patterns.isPattern(word);
        Tab currentTab = mPagerAdapter.getTabForPosition(mViewPager.getCurrentItem());
        // If we're searching for a pattern, open the pattern tab
        if (isPattern) {
            if (currentTab != Tab.PATTERN) {
                Fragment patternTab = mPagerAdapter.getFragment(mViewPager, Tab.PATTERN);
                if (patternTab == null) {
                    mPagerAdapter.setExtraTab(Tab.PATTERN);
                }
                mViewPager.setCurrentItem(mPagerAdapter.getPositionForTab(Tab.PATTERN));
            }
        } else {
            mPagerAdapter.setExtraTab(null);
            // If we're in the pattern tab but not searching for a pattern, go to the rhymer tab.
            if (currentTab != Tab.RHYMER && currentTab != Tab.THESAURUS && currentTab != Tab.DICTIONARY) {
                mViewPager.setCurrentItem(mPagerAdapter.getPositionForTab(Tab.RHYMER));
            }
        }

    }

    /**
     * Lookup a random word. Update the view pager tabs with the results of this word.
     */
    public void lookupRandom() {
        Log.d(TAG, "lookupRandom");
        new AsyncTask<Void, Void, String>() {
            @Override
            protected String doInBackground(Void... voids) {
                DictionaryEntry entry = mDictionary.getRandomEntry();
                return entry == null ? null : entry.word;
            }

            @Override
            protected void onPostExecute(@Nullable String word) {
                if (word != null) {
                    search(word);
                    mViewPager.setCurrentItem(mPagerAdapter.getPositionForTab(Tab.DICTIONARY));
                }
            }
        }.execute();
    }

    /**
     * Adds the given suggestions to the search history, in a background thread.
     */
    @MainThread
    public void addSuggestions(String... suggestions) {
        new AsyncTask<String, Void, Void>() {

            @Override
            protected Void doInBackground(String... searchTerms) {
                ContentValues[] contentValues = new ContentValues[suggestions.length];
                for (int i = 0; i < suggestions.length; i++) {
                    ContentValues contentValue = new ContentValues(1);
                    contentValue.put(SearchManager.QUERY, suggestions[i]);
                    contentValues[i] = contentValue;
                }
                mContext.getContentResolver().bulkInsert(SuggestionsProvider.CONTENT_URI, contentValues);
                return null;
            }
        }.execute(suggestions);
    }

    /**
     * @return the terms cleared from the search history.
     */
    @WorkerThread
    public String[] clearSearchHistory() {
        String[] searchHistory = getSearchHistory();
        mContext.getContentResolver().delete(SuggestionsProvider.CONTENT_URI, null, null);
        return searchHistory;
    }

    private String[] getSearchHistory() {
        Cursor cursor = mContext.getContentResolver().query(SuggestionsProvider.CONTENT_URI, null, null, null,
                null);
        if (cursor != null) {
            try {
                String[] result = new String[cursor.getCount()];
                int columnIndex = cursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1);
                while (cursor.moveToNext()) {
                    result[cursor.getPosition()] = cursor.getString(columnIndex);
                }
                return result;
            } finally {
                cursor.close();
            }
        }
        return new String[0];
    }

}