diff options
author | Fabrice Di Meglio <fdimeglio@google.com> | 2014-04-22 17:23:23 -0700 |
---|---|---|
committer | Fabrice Di Meglio <fdimeglio@google.com> | 2014-04-23 10:35:10 -0700 |
commit | d297a5840230b769a3c7ad8b85232968a7077f64 (patch) | |
tree | 3f60acb915ce48f6ad9168341f36620348327d7c /src/com/android/settings | |
parent | 891bbfdbb729a83d03da1fec0b84407418a84542 (diff) | |
download | packages_apps_Settings-d297a5840230b769a3c7ad8b85232968a7077f64.tar.gz packages_apps_Settings-d297a5840230b769a3c7ad8b85232968a7077f64.tar.bz2 packages_apps_Settings-d297a5840230b769a3c7ad8b85232968a7077f64.zip |
Add saved Search queries feature
- update SearchResultsSummary fragment to have two lists:
one for Search suggestions (saved queries) and one for
Search results
- a tap on a saved query will launch that Search query
- show the list of saved queries when tapping on the SearchView
- do some fancy hidding / unhidding of the saved queries list
and results list
Change-Id: If15055ab78b0ec5eef4e543173dc7b866bd08e27
Diffstat (limited to 'src/com/android/settings')
-rw-r--r-- | src/com/android/settings/SettingsActivity.java | 1 | ||||
-rw-r--r-- | src/com/android/settings/dashboard/SearchResultsSummary.java | 285 | ||||
-rw-r--r-- | src/com/android/settings/search/Index.java | 110 |
3 files changed, 322 insertions, 74 deletions
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java index 964c4448c..4c3e2bc23 100644 --- a/src/com/android/settings/SettingsActivity.java +++ b/src/com/android/settings/SettingsActivity.java @@ -1280,6 +1280,7 @@ public class SettingsActivity extends Activity mSearchResultsFragment = (SearchResultsSummary) switchToFragment( SearchResultsSummary.class.getName(), null, false, true, title, true); } + mSearchResultsFragment.setSearchView(mSearchView); mSearchMenuItemExpanded = true; } diff --git a/src/com/android/settings/dashboard/SearchResultsSummary.java b/src/com/android/settings/dashboard/SearchResultsSummary.java index a7076eaff..89e33f67b 100644 --- a/src/com/android/settings/dashboard/SearchResultsSummary.java +++ b/src/com/android/settings/dashboard/SearchResultsSummary.java @@ -28,7 +28,6 @@ import android.database.sqlite.SQLiteDatabase; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Bundle; -import android.os.Handler; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; @@ -38,6 +37,7 @@ import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.ListView; +import android.widget.SearchView; import android.widget.TextView; import com.android.settings.R; import com.android.settings.SettingsActivity; @@ -54,17 +54,23 @@ public class SearchResultsSummary extends Fragment { private static final String LOG_TAG = "SearchResultsSummary"; + private static final String EMPTY_QUERY = ""; private static char ELLIPSIS = '\u2026'; - private ListView mListView; + private SearchView mSearchView; - private SearchResultsAdapter mAdapter; + private ListView mResultsListView; + private SearchResultsAdapter mResultsAdapter; private UpdateSearchResultsTask mUpdateSearchResultsTask; - private String mQuery; - private SaveSearchQueryTask mSaveSearchQueryTask; + private ListView mSuggestionsListView; + private SuggestionsAdapter mSuggestionsAdapter; + private UpdateSuggestionsTask mUpdateSuggestionsTask; + + private ViewGroup mLayoutSuggestions; + private ViewGroup mLayoutResults; - private static long MAX_SAVED_SEARCH_QUERY = 5; + private String mQuery; /** * A basic AsyncTask for updating the query results cursor @@ -78,7 +84,7 @@ public class SearchResultsSummary extends Fragment { @Override protected void onPostExecute(Cursor cursor) { if (!isCancelled()) { - setCursor(cursor); + setResultsCursor(cursor); } else if (cursor != null) { cursor.close(); } @@ -86,37 +92,21 @@ public class SearchResultsSummary extends Fragment { } /** - * A basic AsynTask for saving the Search query into the database + * A basic AsyncTask for updating the suggestions cursor */ - private class SaveSearchQueryTask extends AsyncTask<String, Void, Long> { + private class UpdateSuggestionsTask extends AsyncTask<String, Void, Cursor> { + @Override + protected Cursor doInBackground(String... params) { + return Index.getInstance(getActivity()).getSuggestions(params[0]); + } @Override - protected Long doInBackground(String... params) { - final long now = new Date().getTime(); - - final ContentValues values = new ContentValues(); - values.put(SavedQueriesColums.QUERY, params[0]); - values.put(SavedQueriesColums.TIME_STAMP, now); - - SQLiteDatabase database = IndexDatabaseHelper.getInstance( - getActivity()).getWritableDatabase(); - - long lastInsertedRowId = -1; - try { - lastInsertedRowId = - database.insert(Tables.TABLE_SAVED_QUERIES, null, values); - - final long delta = lastInsertedRowId - MAX_SAVED_SEARCH_QUERY; - if (delta > 0) { - int count = database.delete(Tables.TABLE_SAVED_QUERIES, "rowId <= ?", - new String[] { Long.toString(delta) }); - Log.d(LOG_TAG, "Deleted '" + count + "' saved Search query(ies)"); - } - } catch (Exception e) { - Log.d(LOG_TAG, "Cannot update saved Search queries", e); + protected void onPostExecute(Cursor cursor) { + if (!isCancelled()) { + setSuggestionsCursor(cursor); + } else if (cursor != null) { + cursor.close(); } - - return lastInsertedRowId; } } @@ -124,22 +114,30 @@ public class SearchResultsSummary extends Fragment { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mAdapter = new SearchResultsAdapter(getActivity()); + mResultsAdapter = new SearchResultsAdapter(getActivity()); + mSuggestionsAdapter = new SuggestionsAdapter(getActivity()); } @Override public void onStop() { super.onStop(); + clearSuggestions(); clearResults(); } @Override public void onDestroy() { - mListView = null; - mAdapter = null; + mResultsListView = null; + mResultsAdapter = null; mUpdateSearchResultsTask = null; + mSuggestionsListView = null; + mSuggestionsAdapter = null; + mUpdateSuggestionsTask = null; + + mSearchView = null; + super.onDestroy(); } @@ -147,14 +145,17 @@ public class SearchResultsSummary extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final View view = inflater.inflate(R.layout.search_results, container, false); + final View view = inflater.inflate(R.layout.search_panel, container, false); - mListView = (ListView) view.findViewById(R.id.list_results); - mListView.setAdapter(mAdapter); - mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + mLayoutSuggestions = (ViewGroup) view.findViewById(R.id.layout_suggestions); + mLayoutResults = (ViewGroup) view.findViewById(R.id.layout_results); + + mResultsListView = (ListView) view.findViewById(R.id.list_results); + mResultsListView.setAdapter(mResultsAdapter); + mResultsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - final Cursor cursor = mAdapter.mCursor; + final Cursor cursor = mResultsAdapter.mCursor; cursor.moveToPosition(position); final String className = cursor.getString(Index.COLUMN_INDEX_CLASS_NAME); @@ -192,33 +193,85 @@ public class SearchResultsSummary extends Fragment { } }); + mSuggestionsListView = (ListView) view.findViewById(R.id.list_suggestions); + mSuggestionsListView.setAdapter(mSuggestionsAdapter); + mSuggestionsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + final Cursor cursor = mSuggestionsAdapter.mCursor; + cursor.moveToPosition(position); + + mQuery = cursor.getString(0); + mSearchView.setQuery(mQuery, false); + setSuggestionsVisibility(false); + } + }); + return view; } - private void saveQueryToDatabase() { - if (mSaveSearchQueryTask != null) { - mSaveSearchQueryTask.cancel(false); - mSaveSearchQueryTask = null; + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + showSomeSuggestions(); + } + + public void setSearchView(SearchView searchView) { + mSearchView = searchView; + } + + private void setSuggestionsVisibility(boolean visible) { + if (mLayoutSuggestions != null) { + mLayoutSuggestions.setVisibility(visible ? View.VISIBLE : View.GONE); } - if (!TextUtils.isEmpty(mQuery)) { - mSaveSearchQueryTask = new SaveSearchQueryTask(); - mSaveSearchQueryTask.execute(mQuery); + } + + private void setResultsVisibility(boolean visible) { + if (mLayoutResults != null) { + mLayoutResults.setVisibility(visible ? View.VISIBLE : View.GONE); } } + private void saveQueryToDatabase() { + Index.getInstance(getActivity()).addSavedQuery(mQuery); + } + public boolean onQueryTextSubmit(String query) { - updateSearchResults(query); + mQuery = getFilteredQueryString(query); + updateSearchResults(); return true; } public boolean onQueryTextChange(String query) { - updateSearchResults(query); + mQuery = getFilteredQueryString(query); + updateSuggestions(); + updateSearchResults(); return true; } - public boolean onClose() { - clearResults(); - return false; + public void showSomeSuggestions() { + setResultsVisibility(false); + mQuery = EMPTY_QUERY; + updateSuggestions(); + } + + private void clearSuggestions() { + if (mUpdateSuggestionsTask != null) { + mUpdateSuggestionsTask.cancel(false); + mUpdateSuggestionsTask = null; + } + setSuggestionsCursor(null); + } + + private void setSuggestionsCursor(Cursor cursor) { + if (mSuggestionsAdapter == null) { + return; + } + Cursor oldCursor = mSuggestionsAdapter.swapCursor(cursor); + if (oldCursor != null) { + oldCursor.close(); + } } private void clearResults() { @@ -226,20 +279,23 @@ public class SearchResultsSummary extends Fragment { mUpdateSearchResultsTask.cancel(false); mUpdateSearchResultsTask = null; } - setCursor(null); + setResultsCursor(null); } - private void setCursor(Cursor cursor) { - if (mAdapter == null) { + private void setResultsCursor(Cursor cursor) { + if (mResultsAdapter == null) { return; } - Cursor oldCursor = mAdapter.swapCursor(cursor); + Cursor oldCursor = mResultsAdapter.swapCursor(cursor); if (oldCursor != null) { oldCursor.close(); } } private String getFilteredQueryString(CharSequence query) { + if (query == null) { + return null; + } final StringBuilder filtered = new StringBuilder(); for (int n = 0; n < query.length(); n++) { char c = query.charAt(n); @@ -251,20 +307,123 @@ public class SearchResultsSummary extends Fragment { return filtered.toString(); } - private void updateSearchResults(CharSequence cs) { + private void updateSuggestions() { + if (mUpdateSuggestionsTask != null) { + mUpdateSuggestionsTask.cancel(false); + mUpdateSuggestionsTask = null; + } + if (mQuery == null) { + setSuggestionsCursor(null); + } else { + setSuggestionsVisibility(true); + mUpdateSuggestionsTask = new UpdateSuggestionsTask(); + mUpdateSuggestionsTask.execute(mQuery); + } + } + + private void updateSearchResults() { if (mUpdateSearchResultsTask != null) { mUpdateSearchResultsTask.cancel(false); mUpdateSearchResultsTask = null; } - mQuery = getFilteredQueryString(cs); if (TextUtils.isEmpty(mQuery)) { - setCursor(null); + setResultsVisibility(false); + setResultsCursor(null); } else { + setResultsVisibility(true); mUpdateSearchResultsTask = new UpdateSearchResultsTask(); mUpdateSearchResultsTask.execute(mQuery); } } + private static class SuggestionItem { + public String query; + + public SuggestionItem(String query) { + this.query = query; + } + } + + private static class SuggestionsAdapter extends BaseAdapter { + + private static final int COLUMN_SUGGESTION_QUERY = 0; + private static final int COLUMN_SUGGESTION_TIMESTAMP = 1; + + private Context mContext; + private Cursor mCursor; + private LayoutInflater mInflater; + private boolean mDataValid = false; + + public SuggestionsAdapter(Context context) { + mContext = context; + mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mDataValid = false; + } + + public Cursor swapCursor(Cursor newCursor) { + if (newCursor == mCursor) { + return null; + } + Cursor oldCursor = mCursor; + mCursor = newCursor; + if (newCursor != null) { + mDataValid = true; + notifyDataSetChanged(); + } else { + mDataValid = false; + notifyDataSetInvalidated(); + } + return oldCursor; + } + + @Override + public int getCount() { + if (!mDataValid || mCursor == null || mCursor.isClosed()) return 0; + return mCursor.getCount(); + } + + @Override + public Object getItem(int position) { + if (mDataValid && mCursor.moveToPosition(position)) { + final String query = mCursor.getString(COLUMN_SUGGESTION_QUERY); + + return new SuggestionItem(query); + } + return null; + } + + @Override + public long getItemId(int position) { + return 0; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (!mDataValid && convertView == null) { + throw new IllegalStateException( + "this should only be called when the cursor is valid"); + } + if (!mCursor.moveToPosition(position)) { + throw new IllegalStateException("couldn't move cursor to position " + position); + } + + View view; + + if (convertView == null) { + view = mInflater.inflate(R.layout.search_suggestion_item, parent, false); + } else { + view = convertView; + } + + TextView query = (TextView) view.findViewById(R.id.title); + + SuggestionItem item = (SuggestionItem) getItem(position); + query.setText(item.query); + + return view; + } + } + private static class SearchResult { public Context context; public String title; @@ -288,10 +447,10 @@ public class SearchResultsSummary extends Fragment { private static class SearchResultsAdapter extends BaseAdapter { + private Context mContext; private Cursor mCursor; private LayoutInflater mInflater; private boolean mDataValid; - private Context mContext; private HashMap<String, Context> mContextMap = new HashMap<String, Context>(); private static final String PERCENT_RECLACE = "%s"; @@ -299,7 +458,7 @@ public class SearchResultsSummary extends Fragment { public SearchResultsAdapter(Context context) { mContext = context; - mInflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mDataValid = false; } diff --git a/src/com/android/settings/search/Index.java b/src/com/android/settings/search/Index.java index 4f5aa2cfb..60660c152 100644 --- a/src/com/android/settings/search/Index.java +++ b/src/com/android/settings/search/Index.java @@ -47,6 +47,7 @@ import java.io.IOException; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -131,6 +132,11 @@ public class Index { IndexColumns.DATA_KEYWORDS }; + // Max number of saved search queries (who will be used for proposing suggestions) + private static long MAX_SAVED_SEARCH_QUERY = 64; + // Max number of proposed suggestions + private static final int MAX_PROPOSED_SUGGESTIONS = 5; + private static final String EMPTY = ""; private static final String NON_BREAKING_HYPHEN = "\u2011"; private static final String HYPHEN = "-"; @@ -144,6 +150,7 @@ public class Index { private static final List<String> EMPTY_LIST = Collections.<String>emptyList(); + private static Index sInstance; private final AtomicBoolean mIsAvailable = new AtomicBoolean(false); private final UpdateData mDataToProcess = new UpdateData(); @@ -198,11 +205,57 @@ public class Index { } public Cursor search(String query) { - final String sql = buildSQL(query); - Log.d(LOG_TAG, "Query: " + sql); + final String sql = buildSearchSQL(query); + Log.d(LOG_TAG, "Search query: " + sql); + return getReadableDatabase().rawQuery(sql, null); + } + + public Cursor getSuggestions(String query) { + final String sql = buildSuggestionsSQL(query); + Log.d(LOG_TAG, "Suggestions query: " + sql); return getReadableDatabase().rawQuery(sql, null); } + private String buildSuggestionsSQL(String query) { + StringBuilder sb = new StringBuilder(); + + sb.append("SELECT "); + sb.append(IndexDatabaseHelper.SavedQueriesColums.QUERY); + sb.append(" FROM "); + sb.append(Tables.TABLE_SAVED_QUERIES); + + if (TextUtils.isEmpty(query)) { + sb.append(" ORDER BY rowId DESC"); + } else { + sb.append(" WHERE "); + sb.append(IndexDatabaseHelper.SavedQueriesColums.QUERY); + sb.append(" LIKE "); + sb.append("'"); + sb.append(query); + sb.append("%"); + sb.append("'"); + } + + sb.append(" LIMIT "); + sb.append(MAX_PROPOSED_SUGGESTIONS); + + return sb.toString(); + } + + public long addSavedQuery(String query){ + final SaveSearchQueryTask task = new SaveSearchQueryTask(); + task.execute(query); + try { + return task.get(); + } catch (InterruptedException e) { + Log.e(LOG_TAG, "Cannot insert saved query: " + query, e); + return -1 ; + } catch (ExecutionException e) { + Log.e(LOG_TAG, "Cannot insert saved query: " + query, e); + return -1; + } + } + public boolean update() { final Intent intent = new Intent(SearchIndexablesContract.PROVIDER_INTERFACE); List<ResolveInfo> list = @@ -432,10 +485,10 @@ public class Index { mDataToProcess.clear(); return result; } catch (InterruptedException e) { - Log.e(LOG_TAG, "Cannot update index: " + e.getMessage()); + Log.e(LOG_TAG, "Cannot update index", e); return false; } catch (ExecutionException e) { - Log.e(LOG_TAG, "Cannot update index: " + e.getMessage()); + Log.e(LOG_TAG, "Cannot update index", e); return false; } } @@ -545,15 +598,15 @@ public class Index { } } - private String buildSQL(String query) { + private String buildSearchSQL(String query) { StringBuilder sb = new StringBuilder(); - sb.append(buildSQLForColumn(query, MATCH_COLUMNS)); + sb.append(buildSearchSQLForColumn(query, MATCH_COLUMNS)); sb.append(" ORDER BY "); sb.append(IndexColumns.DATA_RANK); return sb.toString(); } - private String buildSQLForColumn(String query, String[] columnNames) { + private String buildSearchSQLForColumn(String query, String[] columnNames) { StringBuilder sb = new StringBuilder(); sb.append("SELECT "); for (int n = 0; n < SELECT_COLUMNS.length; n++) { @@ -565,15 +618,16 @@ public class Index { sb.append(" FROM "); sb.append(Tables.TABLE_PREFS_INDEX); sb.append(" WHERE "); - sb.append(buildWhereStringForColumns(query, columnNames)); + sb.append(buildSearchWhereStringForColumns(query, columnNames)); return sb.toString(); } - private String buildWhereStringForColumns(String query, String[] columnNames) { + private String buildSearchWhereStringForColumns(String query, String[] columnNames) { final StringBuilder sb = new StringBuilder(Tables.TABLE_PREFS_INDEX); sb.append(" MATCH "); - DatabaseUtils.appendEscapedSQLString(sb, buildMatchStringForColumns(query, columnNames)); + DatabaseUtils.appendEscapedSQLString(sb, + buildSearchMatchStringForColumns(query, columnNames)); sb.append(" AND "); sb.append(IndexColumns.LOCALE); sb.append(" = "); @@ -584,7 +638,7 @@ public class Index { return sb.toString(); } - private String buildMatchStringForColumns(String query, String[] columnNames) { + private String buildSearchMatchStringForColumns(String query, String[] columnNames) { final String value = query + "*"; StringBuilder sb = new StringBuilder(); final int count = columnNames.length; @@ -1144,4 +1198,38 @@ public class Index { return result; } } + + /** + * A basic AsynTask for saving a Search query into the database + */ + private class SaveSearchQueryTask extends AsyncTask<String, Void, Long> { + + @Override + protected Long doInBackground(String... params) { + final long now = new Date().getTime(); + + final ContentValues values = new ContentValues(); + values.put(IndexDatabaseHelper.SavedQueriesColums.QUERY, params[0]); + values.put(IndexDatabaseHelper.SavedQueriesColums.TIME_STAMP, now); + + final SQLiteDatabase database = getWritableDatabase(); + + long lastInsertedRowId = -1; + try { + lastInsertedRowId = + database.replaceOrThrow(Tables.TABLE_SAVED_QUERIES, null, values); + + final long delta = lastInsertedRowId - MAX_SAVED_SEARCH_QUERY; + if (delta > 0) { + int count = database.delete(Tables.TABLE_SAVED_QUERIES, "rowId <= ?", + new String[] { Long.toString(delta) }); + Log.d(LOG_TAG, "Deleted '" + count + "' saved Search query(ies)"); + } + } catch (Exception e) { + Log.d(LOG_TAG, "Cannot update saved Search queries", e); + } + + return lastInsertedRowId; + } + } } |