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 MaterialSearchViewListAdapter mAdapter;
53    private QuerySuggestionsTask mQueryTask;
54
55    public MaterialSearchSuggestionsList(Context context) {
56        super(context);
57    }
58
59    public MaterialSearchSuggestionsList(Context context, AttributeSet attrs) {
60        super(context, attrs);
61    }
62
63    // PUBLIC API
64    public void setController(MaterialSearchViewController controller,
65            SearchRecentSuggestionsProvider suggestionsProvider) {
66        mController = controller;
67        mSuggestionsProvider = suggestionsProvider;
68    }
69
70    public void setQuery(String query) {
71        mQuery = query;
72        if (mQueryTask != null) {
73            mQueryTask.cancel(true);
74        }
75        mQueryTask = new QuerySuggestionsTask();
76        mQueryTask.execute(query);
77    }
78
79    // PRIVATE API
80    @Override
81    protected void onFinishInflate() {
82        super.onFinishInflate();
83
84        final ListView listView = (ListView) findViewById(R.id.search_overlay_suggestion_list);
85        listView.setOnItemClickListener(this);
86        final View dummyHolder = findViewById(R.id.search_overlay_scrim);
87        dummyHolder.setOnClickListener(this);
88
89        // set up the adapter
90        mAdapter = new MaterialSearchViewListAdapter(getContext(), R.layout.search_suggestion_item);
91        listView.setAdapter(mAdapter);
92    }
93
94    @Override
95    public void setVisibility(int visibility) {
96        if (!isShown() && visibility == VISIBLE) {
97            // When we go from gone to visible, re-query for suggestions in case they changed.
98            setQuery(mQuery);
99        }
100        super.setVisibility(visibility);
101    }
102
103    @Override
104    public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
105        mController.onSearchPerformed(mSuggestions.get(position).suggestion);
106    }
107
108    @Override
109    public void onClick(View view) {
110        mController.showSearchActionBar(
111                MaterialSearchViewController.SEARCH_VIEW_STATE_ONLY_ACTIONBAR);
112    }
113
114    // Background task for querying the suggestions list
115    private class QuerySuggestionsTask extends AsyncTask<String, Void, List<SuggestionItem>> {
116        @Override
117        protected List<SuggestionItem> doInBackground(String... strings) {
118            String query = strings[0];
119            if (query == null) {
120                query = "";
121            }
122
123            Cursor c = null;
124            final List<SuggestionItem> result = Lists.newArrayList();
125            try {
126                c = mSuggestionsProvider.query(query);
127
128                if (c != null && c.moveToFirst()) {
129                    final int textIndex = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_QUERY);
130                    final int iconIndex = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1);
131                    do {
132                        final String suggestion = c.getString(textIndex);
133                        final Uri iconUri = Uri.parse(c.getString(iconIndex));
134                        result.add(new SuggestionItem(suggestion, iconUri));
135                    } while (c.moveToNext());
136                }
137            } finally {
138                if (c != null) {
139                    c.close();
140                }
141            }
142
143            return result;
144        }
145
146        @Override
147        protected void onPostExecute(List<SuggestionItem> strings) {
148            if (!isCancelled()) {
149                // Should not have any race conditions here since we cancel the previous asynctask
150                // before starting the new one. It's unlikely that the new task finishes fast enough
151                // to get to onPostExecute when this one is in addAll.
152                mSuggestions.clear();
153                mSuggestions.addAll(strings);
154                mAdapter.notifyDataSetChanged();
155            }
156        }
157    }
158
159    private static class SuggestionItem {
160        final String suggestion;
161        final Uri icon;
162
163        public SuggestionItem(String s, Uri i) {
164            suggestion = s;
165            icon = i;
166        }
167    }
168
169    // Custom adapter to populate our list
170    private class MaterialSearchViewListAdapter extends BaseAdapter {
171        private final Context mContext;
172        private final int mResId;
173        private LayoutInflater mInflater;
174
175        public MaterialSearchViewListAdapter(Context context, int resource) {
176            super();
177            mContext = context;
178            mResId = resource;
179        }
180
181        private LayoutInflater getInflater() {
182            if (mInflater == null) {
183                mInflater = LayoutInflater.from(mContext);
184            }
185            return mInflater;
186        }
187
188        @Override
189        public int getCount() {
190            return mSuggestions.size();
191        }
192
193        @Override
194        public Object getItem(int i) {
195            return mSuggestions.get(i);
196        }
197
198        @Override
199        public long getItemId(int i) {
200            return 0;
201        }
202
203        @Override
204        public View getView(int position, View convertView, ViewGroup parent) {
205            if (convertView == null) {
206                convertView = getInflater().inflate(mResId, parent, false);
207            }
208
209            final SuggestionItem item = mSuggestions.get(position);
210            final TextView text =
211                    (TextView) convertView.findViewById(R.id.search_overlay_item_text);
212            text.setText(item.suggestion);
213            text.setContentDescription(getResources().getString(R.string.search_suggestion_desc,
214                    item.suggestion));
215            ((ImageView) convertView.findViewById(R.id.search_overlay_item_icon))
216                    .setImageURI(item.icon);
217
218            return convertView;
219        }
220    }
221}
222