DefaultSuggestionView.java revision 2fb3a129925a42e72944b836e85a1a2d55a0d950
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.refine_query); 131 setIcon2(icon2); 132 } else { 133 mIcon2.setOnClickListener(null); 134 } 135 } 136 137 private CharSequence formatUrl(CharSequence url) { 138 SpannableString text = new SpannableString(url); 139 ColorStateList colors = getResources().getColorStateList(R.color.url_text); 140 text.setSpan(new TextAppearanceSpan(null, 0, 0, colors, null), 141 0, url.length(), 142 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 143 return text; 144 } 145 146 public Drawable getSuggestionDrawableIcon1(Suggestion suggestion) { 147 Source source = suggestion.getSuggestionSource(); 148 String iconId = suggestion.getSuggestionIcon1(); 149 Drawable icon1 = iconId == null ? null : source.getIcon(iconId); 150 return icon1 == null ? source.getSourceIcon() : icon1; 151 } 152 153 public Drawable getSuggestionDrawableIcon2(Suggestion suggestion) { 154 Source source = suggestion.getSuggestionSource(); 155 String iconId = suggestion.getSuggestionIcon2(); 156 return iconId == null ? null : source.getIcon(iconId); 157 } 158 159 private CharSequence formatText(String str, SuggestionCursor suggestion, 160 boolean highlightSuggested) { 161 boolean isHtml = "html".equals(suggestion.getSuggestionFormat()); 162 if (isHtml && looksLikeHtml(str)) { 163 return Html.fromHtml(str); 164 } else if (highlightSuggested && suggestion.isWebSearchSuggestion() && 165 !TextUtils.isEmpty(suggestion.getUserQuery())) { 166 return mSuggestionFormatter.formatSuggestion(suggestion.getUserQuery(), str); 167 } else { 168 return str; 169 } 170 } 171 172 private boolean looksLikeHtml(String str) { 173 if (TextUtils.isEmpty(str)) return false; 174 for (int i = str.length() - 1; i >= 0; i--) { 175 char c = str.charAt(i); 176 if (c == '>' || c == '&') return true; 177 } 178 return false; 179 } 180 181 /** 182 * Sets the first text line. 183 */ 184 private void setText1(CharSequence text) { 185 mText1.setText(text); 186 } 187 188 /** 189 * Sets the second text line. 190 */ 191 private void setText2(CharSequence text) { 192 mText2.setText(text); 193 if (TextUtils.isEmpty(text)) { 194 mText2.setVisibility(GONE); 195 } else { 196 mText2.setVisibility(VISIBLE); 197 } 198 } 199 200 /** 201 * Sets the left-hand-side icon. 202 */ 203 private void setIcon1(Drawable icon) { 204 setViewDrawable(mIcon1, icon); 205 } 206 207 /** 208 * Sets the right-hand-side icon. 209 */ 210 private void setIcon2(Drawable icon) { 211 setViewDrawable(mIcon2, icon); 212 } 213 214 /** 215 * Sets the drawable in an image view, makes sure the view is only visible if there 216 * is a drawable. 217 */ 218 private static void setViewDrawable(ImageView v, Drawable drawable) { 219 // Set the icon even if the drawable is null, since we need to clear any 220 // previous icon. 221 v.setImageDrawable(drawable); 222 223 if (drawable == null) { 224 v.setVisibility(View.GONE); 225 } else { 226 v.setVisibility(View.VISIBLE); 227 228 // This is a hack to get any animated drawables (like a 'working' spinner) 229 // to animate. You have to setVisible true on an AnimationDrawable to get 230 // it to start animating, but it must first have been false or else the 231 // call to setVisible will be ineffective. We need to clear up the story 232 // about animated drawables in the future, see http://b/1878430. 233 drawable.setVisible(false, false); 234 drawable.setVisible(true, false); 235 } 236 } 237 238} 239