DefaultSuggestionView.java revision 6b8dba4c144efb18faa9720d022db2aa7a532f59
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.quicksearchbox.ui; 18 19import com.android.quicksearchbox.QsbApplication; 20import com.android.quicksearchbox.R; 21import com.android.quicksearchbox.Source; 22import com.android.quicksearchbox.Suggestion; 23import com.android.quicksearchbox.SuggestionCursor; 24import com.android.quicksearchbox.SuggestionFormatter; 25 26import android.content.Context; 27import android.content.res.ColorStateList; 28import android.graphics.drawable.Drawable; 29import android.text.Html; 30import android.text.Spannable; 31import android.text.SpannableString; 32import android.text.TextUtils; 33import android.text.style.TextAppearanceSpan; 34import android.util.AttributeSet; 35import android.util.Log; 36import android.view.View; 37import android.widget.ImageView; 38import android.widget.RelativeLayout; 39import android.widget.TextView; 40 41/** 42 * View for the items in the suggestions list. This includes promoted suggestions, 43 * sources, and suggestions under each source. 44 * 45 */ 46public class DefaultSuggestionView extends RelativeLayout implements SuggestionView { 47 48 private static final boolean DBG = false; 49 private static final String TAG = "QSB.SuggestionView"; 50 51 private TextView mText1; 52 private TextView mText2; 53 private ImageView mIcon1; 54 private ImageView mIcon2; 55 private final SuggestionFormatter mSuggestionFormatter; 56 57 public DefaultSuggestionView(Context context, AttributeSet attrs, int defStyle) { 58 super(context, attrs, defStyle); 59 mSuggestionFormatter = QsbApplication.get(context).getSuggestionFormatter(); 60 } 61 62 public DefaultSuggestionView(Context context, AttributeSet attrs) { 63 super(context, attrs); 64 mSuggestionFormatter = QsbApplication.get(context).getSuggestionFormatter(); 65 } 66 67 public DefaultSuggestionView(Context context) { 68 super(context); 69 mSuggestionFormatter = QsbApplication.get(context).getSuggestionFormatter(); 70 } 71 72 @Override 73 protected void onFinishInflate() { 74 super.onFinishInflate(); 75 mText1 = (TextView) findViewById(R.id.text1); 76 mText2 = (TextView) findViewById(R.id.text2); 77 mIcon1 = (ImageView) findViewById(R.id.icon1); 78 mIcon2 = (ImageView) findViewById(R.id.icon2); 79 } 80 81 public void bindAsSuggestion(SuggestionCursor suggestion) { 82 CharSequence text1 = formatText(suggestion.getSuggestionText1(), suggestion, true); 83 CharSequence text2 = suggestion.getSuggestionText2Url(); 84 if (text2 != null) { 85 text2 = formatUrl(text2); 86 } else { 87 text2 = formatText(suggestion.getSuggestionText2(), suggestion, false); 88 } 89 Drawable icon1 = getSuggestionDrawableIcon1(suggestion); 90 Drawable icon2 = getSuggestionDrawableIcon2(suggestion); 91 if (DBG) { 92 Log.d(TAG, "bindAsSuggestion(), text1=" + text1 + ",text2=" + text2 93 + ",icon1=" + icon1 + ",icon2=" + icon2); 94 } 95 // If there is no text for the second line, allow the first line to be up to two lines 96 if (TextUtils.isEmpty(text2)) { 97 mText1.setSingleLine(false); 98 mText1.setMaxLines(2); 99 mText1.setEllipsize(TextUtils.TruncateAt.START); 100 } else { 101 mText1.setSingleLine(true); 102 mText1.setMaxLines(1); 103 mText1.setEllipsize(TextUtils.TruncateAt.MIDDLE); 104 } 105 setText1(text1); 106 setText2(text2); 107 setIcon1(icon1); 108 setIcon2(icon2); 109 updateRefinable(suggestion); 110 } 111 112 protected void updateRefinable(SuggestionCursor suggestion) { 113 boolean refinable = 114 suggestion.isWebSearchSuggestion() 115 && mIcon2.getDrawable() == null 116 && !TextUtils.isEmpty(suggestion.getSuggestionQuery()); 117 setRefinable(suggestion, refinable); 118 } 119 120 protected void setRefinable(SuggestionCursor suggestion, boolean refinable) { 121 if (refinable) { 122 final int position = suggestion.getPosition(); 123 mIcon2.setOnClickListener(new View.OnClickListener() { 124 public void onClick(View v) { 125 Log.d(TAG, "Clicked query refine"); 126 SuggestionsView suggestions = (SuggestionsView) getParent(); 127 suggestions.onIcon2Clicked(position); 128 } 129 }); 130 Drawable icon2 = getContext().getResources().getDrawable(R.drawable.edit_query); 131 Drawable background = 132 getContext().getResources().getDrawable(R.drawable.edit_query_background); 133 setIcon2(icon2, background); 134 } else { 135 mIcon2.setOnClickListener(null); 136 } 137 } 138 139 private CharSequence formatUrl(CharSequence url) { 140 SpannableString text = new SpannableString(url); 141 ColorStateList colors = getResources().getColorStateList(R.color.url_text); 142 text.setSpan(new TextAppearanceSpan(null, 0, 0, colors, null), 143 0, url.length(), 144 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 145 return text; 146 } 147 148 public Drawable getSuggestionDrawableIcon1(Suggestion suggestion) { 149 Source source = suggestion.getSuggestionSource(); 150 String iconId = suggestion.getSuggestionIcon1(); 151 Drawable icon1 = iconId == null ? null : source.getIcon(iconId); 152 return icon1 == null ? source.getSourceIcon() : icon1; 153 } 154 155 public Drawable getSuggestionDrawableIcon2(Suggestion suggestion) { 156 Source source = suggestion.getSuggestionSource(); 157 String iconId = suggestion.getSuggestionIcon2(); 158 return iconId == null ? null : source.getIcon(iconId); 159 } 160 161 private CharSequence formatText(String str, SuggestionCursor suggestion, 162 boolean highlightSuggested) { 163 boolean isHtml = "html".equals(suggestion.getSuggestionFormat()); 164 if (isHtml && looksLikeHtml(str)) { 165 return Html.fromHtml(str); 166 } else if (highlightSuggested && suggestion.isWebSearchSuggestion() && 167 !TextUtils.isEmpty(suggestion.getUserQuery())) { 168 return mSuggestionFormatter.formatSuggestion(suggestion.getUserQuery(), str); 169 } else { 170 return str; 171 } 172 } 173 174 private boolean looksLikeHtml(String str) { 175 if (TextUtils.isEmpty(str)) return false; 176 for (int i = str.length() - 1; i >= 0; i--) { 177 char c = str.charAt(i); 178 if (c == '>' || c == '&') return true; 179 } 180 return false; 181 } 182 183 /** 184 * Sets the first text line. 185 */ 186 private void setText1(CharSequence text) { 187 mText1.setText(text); 188 } 189 190 /** 191 * Sets the second text line. 192 */ 193 private void setText2(CharSequence text) { 194 mText2.setText(text); 195 if (TextUtils.isEmpty(text)) { 196 mText2.setVisibility(GONE); 197 } else { 198 mText2.setVisibility(VISIBLE); 199 } 200 } 201 202 /** 203 * Sets the left-hand-side icon. 204 */ 205 private void setIcon1(Drawable icon) { 206 setViewDrawable(mIcon1, icon); 207 } 208 209 /** 210 * Sets the right-hand-side icon. 211 */ 212 private void setIcon2(Drawable icon) { 213 setViewDrawable(mIcon2, icon); 214 } 215 216 /** 217 * Sets the right-hand-side icon and its background. 218 */ 219 private void setIcon2(Drawable icon, Drawable background) { 220 setViewDrawable(mIcon2, icon); 221 mIcon2.setBackgroundDrawable(background); 222 } 223 224 /** 225 * Sets the drawable in an image view, makes sure the view is only visible if there 226 * is a drawable. 227 */ 228 private static void setViewDrawable(ImageView v, Drawable drawable) { 229 // Set the icon even if the drawable is null, since we need to clear any 230 // previous icon. 231 v.setImageDrawable(drawable); 232 233 if (drawable == null) { 234 v.setVisibility(View.GONE); 235 } else { 236 v.setVisibility(View.VISIBLE); 237 238 // This is a hack to get any animated drawables (like a 'working' spinner) 239 // to animate. You have to setVisible true on an AnimationDrawable to get 240 // it to start animating, but it must first have been false or else the 241 // call to setVisible will be ineffective. We need to clear up the story 242 // about animated drawables in the future, see http://b/1878430. 243 drawable.setVisible(false, false); 244 drawable.setVisible(true, false); 245 } 246 } 247 248} 249