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 18 package com.android.mail.ui; 19 20 import android.app.SearchManager; 21 import android.content.Context; 22 import android.database.Cursor; 23 import android.net.Uri; 24 import android.os.AsyncTask; 25 import android.util.AttributeSet; 26 import android.view.LayoutInflater; 27 import android.view.View; 28 import android.view.ViewGroup; 29 import android.widget.AdapterView; 30 import android.widget.BaseAdapter; 31 import android.widget.ImageView; 32 import android.widget.LinearLayout; 33 import android.widget.ListView; 34 import android.widget.TextView; 35 36 import com.android.mail.R; 37 import com.android.mail.providers.SearchRecentSuggestionsProvider; 38 import com.google.common.collect.Lists; 39 40 import java.util.List; 41 42 /** 43 * Custom quantum-styled search view that overlays the main activity. 44 */ 45 public 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