MaterialSearchSuggestionsList.java revision 37d8ca1eec6c9e3b03eb9036427121ece11fbf5a
1/*
2 * Copyright (C) 2014 Google Inc.
3 * Licensed to The Android Open Source Project.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package com.android.mail.ui;
19
20import android.app.SearchManager;
21import android.content.Context;
22import android.database.Cursor;
23import android.net.Uri;
24import android.os.AsyncTask;
25import android.util.AttributeSet;
26import android.view.LayoutInflater;
27import android.view.View;
28import android.view.ViewGroup;
29import android.widget.AdapterView;
30import android.widget.BaseAdapter;
31import android.widget.ImageView;
32import android.widget.LinearLayout;
33import android.widget.ListView;
34import android.widget.TextView;
35
36import com.android.mail.R;
37import com.android.mail.providers.SearchRecentSuggestionsProvider;
38import com.google.common.collect.Lists;
39
40import java.util.List;
41
42/**
43 * Custom quantum-styled search view that overlays the main activity.
44 */
45public class MaterialSearchSuggestionsList extends LinearLayout
46        implements AdapterView.OnItemClickListener, View.OnClickListener {
47    private MaterialSearchViewController mController;
48    private SearchRecentSuggestionsProvider mSuggestionsProvider;
49    private List<SuggestionItem> mSuggestions = Lists.newArrayList();
50    private String mQuery;
51
52    private View mDummyHolder;
53    private ListView mListView;
54    private MaterialSearchViewListAdapter mAdapter;
55    private QuerySuggestionsTask mQueryTask;
56
57    public MaterialSearchSuggestionsList(Context context) {
58        super(context);
59    }
60
61    public MaterialSearchSuggestionsList(Context context, AttributeSet attrs) {
62        super(context, attrs);
63    }
64
65    // PUBLIC API
66    public void setController(MaterialSearchViewController controller,
67            SearchRecentSuggestionsProvider suggestionsProvider) {
68        mController = controller;
69        mSuggestionsProvider = suggestionsProvider;
70    }
71
72    public void setQuery(String query) {
73        mQuery = query;
74        if (mQueryTask != null) {
75            mQueryTask.cancel(true);
76        }
77        mQueryTask = new QuerySuggestionsTask();
78        mQueryTask.execute(query);
79    }
80
81    // PRIVATE API
82    @Override
83    protected void onFinishInflate() {
84        super.onFinishInflate();
85
86        mListView = (ListView) findViewById(R.id.search_overlay_suggestion_list);
87        mListView.setOnItemClickListener(this);
88        mDummyHolder = findViewById(R.id.search_overlay_scrim);
89        mDummyHolder.setOnClickListener(this);
90
91        // set up the adapter
92        mAdapter = new MaterialSearchViewListAdapter(getContext(), R.layout.search_suggestion_item);
93        mListView.setAdapter(mAdapter);
94    }
95
96    @Override
97    public void setVisibility(int visibility) {
98        if (!isShown() && visibility == VISIBLE) {
99            // When we go from gone to visible, re-query for suggestions in case they changed.
100            setQuery(mQuery);
101        }
102        super.setVisibility(visibility);
103    }
104
105    @Override
106    public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
107        mController.onSearchPerformed(mSuggestions.get(position).suggestion);
108    }
109
110    @Override
111    public void onClick(View view) {
112        mController.showSearchActionBar(
113                MaterialSearchViewController.SEARCH_VIEW_STATE_ONLY_ACTIONBAR);
114    }
115
116    // Background task for querying the suggestions list
117    private class QuerySuggestionsTask extends AsyncTask<String, Void, List<SuggestionItem>> {
118        @Override
119        protected List<SuggestionItem> doInBackground(String... strings) {
120            String query = strings[0];
121            if (query == null) {
122                query = "";
123            }
124
125            Cursor c = null;
126            final List<SuggestionItem> result = Lists.newArrayList();
127            try {
128                c = mSuggestionsProvider.query(null, "query LIKE ?",
129                        new String[] { query }, null);
130
131                if (c != null && c.moveToFirst()) {
132                    final int textIndex = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_QUERY);
133                    final int iconIndex = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1);
134                    do {
135                        final String suggestion = c.getString(textIndex);
136                        final Uri iconUri = Uri.parse(c.getString(iconIndex));
137                        result.add(new SuggestionItem(suggestion, iconUri));
138                    } while (c.moveToNext());
139                }
140            } finally {
141                if (c != null) {
142                    c.close();
143                }
144            }
145
146            return result;
147        }
148
149        @Override
150        protected void onPostExecute(List<SuggestionItem> strings) {
151            if (!isCancelled()) {
152                // Should not have any race conditions here since we cancel the previous asynctask
153                // before starting the new one. It's unlikely that the new task finishes fast enough
154                // to get to onPostExecute when this one is in addAll.
155                mSuggestions.clear();
156                mSuggestions.addAll(strings);
157                mAdapter.notifyDataSetChanged();
158            }
159        }
160    }
161
162    private static class SuggestionItem {
163        final String suggestion;
164        final Uri icon;
165
166        public SuggestionItem(String s, Uri i) {
167            suggestion = s;
168            icon = i;
169        }
170    }
171
172    // Custom adapter to populate our list
173    private class MaterialSearchViewListAdapter extends BaseAdapter {
174        private final Context mContext;
175        private final int mResId;
176        private LayoutInflater mInflater;
177
178        public MaterialSearchViewListAdapter(Context context, int resource) {
179            super();
180            mContext = context;
181            mResId = resource;
182        }
183
184        private LayoutInflater getInflater() {
185            if (mInflater == null) {
186                mInflater = LayoutInflater.from(mContext);
187            }
188            return mInflater;
189        }
190
191        @Override
192        public int getCount() {
193            return mSuggestions.size();
194        }
195
196        @Override
197        public Object getItem(int i) {
198            return mSuggestions.get(i);
199        }
200
201        @Override
202        public long getItemId(int i) {
203            return 0;
204        }
205
206        @Override
207        public View getView(int position, View convertView, ViewGroup parent) {
208            if (convertView == null) {
209                convertView = getInflater().inflate(mResId, parent, false);
210            }
211
212            final SuggestionItem item = mSuggestions.get(position);
213            ((TextView) convertView.findViewById(R.id.search_overlay_item_text))
214                    .setText(item.suggestion);
215            ((ImageView) convertView.findViewById(R.id.search_overlay_item_icon))
216                    .setImageURI(item.icon);
217
218            return convertView;
219        }
220    }
221}
222